Skip to content

Commit 64590f7

Browse files
committed
fix(grids): handle double tap to allow editing on iOS #2538
Since iOS WebKit doesn't support dblclick and Angular's Hammer plugin doesn't have doubletap exposed, resorting to our Hammer manager. Also prevented the double tap to zoom on the cell as it was rather jarring.
1 parent 76c6649 commit 64590f7

File tree

5 files changed

+63
-12
lines changed

5 files changed

+63
-12
lines changed

projects/igniteui-angular/src/lib/core/touch.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ export class HammerGesturesManager {
2020
inputClass: Hammer.TouchInput,
2121
recognizers: [
2222
[ Hammer.Pan, { threshold: 0 } ],
23-
[ Hammer.Pinch, { enable: true } ],
24-
[ Hammer.Rotate, { enable: true } ],
2523
[ Hammer.Swipe, {
2624
direction: Hammer.DIRECTION_HORIZONTAL
27-
}]
25+
}],
26+
[Hammer.Tap],
27+
[Hammer.Tap, { event: 'doubletap', taps: 2 }, ['tap']]
2828
]
2929
};
3030

@@ -44,14 +44,14 @@ export class HammerGesturesManager {
4444
public addEventListener(element: HTMLElement,
4545
eventName: string,
4646
eventHandler: (eventObj) => void,
47-
options: object = null): () => void {
47+
options: HammerOptions = null): () => void {
4848

4949
// Creating the manager bind events, must be done outside of angular
5050
return this._zone.runOutsideAngular(() => {
5151
let mc: HammerManager = this.getManagerForElement(element);
5252
if (mc === null) {
5353
// new Hammer is a shortcut for Manager with defaults
54-
mc = new Hammer(element, this.hammerOptions);
54+
mc = new Hammer(element, Object.assign(this.hammerOptions, options));
5555
this.addManagerForElement(element, mc);
5656
}
5757
const handler = (eventObj) => { this._zone.run(() => { eventHandler(eventObj); }); };

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

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { State } from '../services/index';
2323
import { IgxGridBaseComponent, IGridEditEventArgs, IGridDataBindable } from './grid-base.component';
2424
import { IgxGridSelectionService, ISelectionNode, IgxGridCRUDService } from '../core/grid-selection';
2525
import { DeprecateProperty } from '../core/deprecateDecorators';
26+
import { HammerGesturesManager } from '../core/touch';
2627

2728
/**
2829
* Providing reference to `IgxGridCellComponent`:
@@ -40,7 +41,8 @@ import { DeprecateProperty } from '../core/deprecateDecorators';
4041
@Component({
4142
changeDetection: ChangeDetectionStrategy.OnPush,
4243
selector: 'igx-grid-cell',
43-
templateUrl: './cell.component.html'
44+
templateUrl: './cell.component.html',
45+
providers: [HammerGesturesManager]
4446
})
4547
export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy {
4648
private _vIndex = -1;
@@ -530,7 +532,8 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy {
530532
public selection: IgxSelectionAPIService,
531533
public cdr: ChangeDetectorRef,
532534
private element: ElementRef,
533-
protected zone: NgZone) { }
535+
protected zone: NgZone,
536+
private touchManager: HammerGesturesManager) { }
534537

535538

536539
/**
@@ -552,6 +555,9 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy {
552555
this.nativeElement.addEventListener('compositionend', this.compositionEndHandler);
553556
}
554557
});
558+
this.touchManager.addEventListener(this.nativeElement, 'doubletap', this.onDoubleClick, {
559+
cssProps: { } /* don't disable user-select, etc */
560+
} as HammerOptions);
555561
}
556562

557563
/**
@@ -569,6 +575,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy {
569575
this.nativeElement.removeEventListener('compositionend', this.compositionEndHandler);
570576
}
571577
});
578+
this.touchManager.destroy();
572579
}
573580

574581
/**
@@ -716,7 +723,11 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy {
716723
* @internal
717724
*/
718725
@HostListener('dblclick', ['$event'])
719-
public onDoubleClick(event: MouseEvent) {
726+
public onDoubleClick = (event: MouseEvent| HammerInput) => {
727+
if (event.type === 'doubletap') {
728+
// prevent double-tap to zoom on iOS
729+
event.preventDefault();
730+
}
720731
if (this.editable && !this.editMode && !this.row.deleted) {
721732
this.crudService.begin(this);
722733
}

projects/igniteui-angular/src/lib/grids/grid/cell.spec.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { UIInteractions, wait } from '../../test-utils/ui-interactions.spec';
88
import { configureTestSuite } from '../../test-utils/configure-suite';
99
import { IgxStringFilteringOperand } from '../../data-operations/filtering-condition';
1010
import { SampleTestData } from '../../test-utils/sample-test-data.spec';
11+
import { HammerGesturesManager } from '../../core/touch';
1112

1213
const DEBOUNCETIME = 30;
1314

@@ -171,6 +172,7 @@ describe('IgxGrid - Cell component', () => {
171172

172173
spyOn(grid.onDoubleClick, 'emit').and.callThrough();
173174
const event = new Event('dblclick');
175+
spyOn(event, 'preventDefault');
174176
cellElem.nativeElement.dispatchEvent(event);
175177
const args: IGridCellEventArgs = {
176178
cell: firstCell,
@@ -179,6 +181,38 @@ describe('IgxGrid - Cell component', () => {
179181

180182
fix.detectChanges();
181183

184+
expect(event.preventDefault).not.toHaveBeenCalled();
185+
expect(grid.onDoubleClick.emit).toHaveBeenCalledWith(args);
186+
expect(firstCell).toBe(fix.componentInstance.clickedCell);
187+
});
188+
189+
it('Should handle doubletap, trigger onDoubleClick event', () => {
190+
const addListenerSpy = spyOn(HammerGesturesManager.prototype, 'addEventListener');
191+
const fix = TestBed.createComponent(DefaultGridComponent);
192+
fix.detectChanges();
193+
194+
const grid = fix.componentInstance.instance;
195+
const cellElem = fix.debugElement.query(By.css(CELL_CSS_CLASS));
196+
const firstCell = grid.getCellByColumn(0, 'index');
197+
198+
// should attach 'doubletap'
199+
expect(addListenerSpy.calls.count()).toBeGreaterThan(1);
200+
expect(addListenerSpy).toHaveBeenCalledWith(firstCell.nativeElement, 'doubletap', firstCell.onDoubleClick, { cssProps: { } });
201+
202+
spyOn(grid.onDoubleClick, 'emit').and.callThrough();
203+
204+
const event = {
205+
type: 'doubletap',
206+
preventDefault: jasmine.createSpy('preventDefault')
207+
};
208+
firstCell.onDoubleClick(event as any);
209+
const args: IGridCellEventArgs = {
210+
cell: firstCell,
211+
event
212+
} as any;
213+
214+
fix.detectChanges();
215+
expect(event.preventDefault).toHaveBeenCalled();
182216
expect(grid.onDoubleClick.emit).toHaveBeenCalledWith(args);
183217
expect(firstCell).toBe(fix.componentInstance.clickedCell);
184218
});

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ import { ChangeDetectorRef, ElementRef, ChangeDetectionStrategy, Component,
55
import { IgxHierarchicalGridComponent } from './hierarchical-grid.component';
66
import { IgxHierarchicalSelectionAPIService } from './selection';
77
import { IgxGridSelectionService, IgxGridCRUDService } from '../../core/grid-selection';
8+
import { HammerGesturesManager } from '../../core/touch';
89

910
@Component({
1011
changeDetection: ChangeDetectionStrategy.OnPush,
1112
preserveWhitespaces: false,
1213
selector: 'igx-hierarchical-grid-cell',
13-
templateUrl: './../cell.component.html'
14+
templateUrl: './../cell.component.html',
15+
providers: [HammerGesturesManager]
1416
})
1517
export class IgxHierarchicalGridCellComponent extends IgxGridCellComponent implements OnInit {
1618

@@ -25,8 +27,9 @@ export class IgxHierarchicalGridCellComponent extends IgxGridCellComponent imple
2527
public cdr: ChangeDetectorRef,
2628
private helement: ElementRef,
2729
protected zone: NgZone,
30+
touchManager: HammerGesturesManager
2831
) {
29-
super(selectionService, crudService, gridAPI, selection, cdr, helement, zone);
32+
super(selectionService, crudService, gridAPI, selection, cdr, helement, zone, touchManager);
3033
this.hSelection = <IgxHierarchicalSelectionAPIService>selection;
3134
}
3235

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ import { getNodeSizeViaRange } from '../../core/utils';
77
import { DOCUMENT } from '@angular/common';
88
import { IgxGridBaseComponent, IGridDataBindable } from '../grid';
99
import { IgxGridSelectionService, IgxGridCRUDService } from '../../core/grid-selection';
10+
import { HammerGesturesManager } from '../../core/touch';
1011

1112
@Component({
1213
changeDetection: ChangeDetectionStrategy.OnPush,
1314
selector: 'igx-tree-grid-cell',
14-
templateUrl: 'tree-cell.component.html'
15+
templateUrl: 'tree-cell.component.html',
16+
providers: [HammerGesturesManager]
1517
})
1618
export class IgxTreeGridCellComponent extends IgxGridCellComponent implements OnInit {
1719
private treeGridAPI: IgxTreeGridAPIService;
@@ -24,8 +26,9 @@ export class IgxTreeGridCellComponent extends IgxGridCellComponent implements On
2426
cdr: ChangeDetectorRef,
2527
element: ElementRef,
2628
protected zone: NgZone,
29+
touchManager: HammerGesturesManager,
2730
@Inject(DOCUMENT) public document) {
28-
super(selectionService, crudService, gridAPI, selection, cdr, element, zone);
31+
super(selectionService, crudService, gridAPI, selection, cdr, element, zone, touchManager);
2932
this.treeGridAPI = <IgxTreeGridAPIService>gridAPI;
3033
}
3134

0 commit comments

Comments
 (0)