Skip to content

Commit 937968d

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 e095f2b commit 937968d

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
@@ -19,11 +19,11 @@ export class HammerGesturesManager {
1919
inputClass: Hammer.TouchInput,
2020
recognizers: [
2121
[ Hammer.Pan, { threshold: 0 } ],
22-
[ Hammer.Pinch, { enable: true } ],
23-
[ Hammer.Rotate, { enable: true } ],
2422
[ Hammer.Swipe, {
2523
direction: Hammer.DIRECTION_HORIZONTAL
26-
}]
24+
}],
25+
[Hammer.Tap],
26+
[Hammer.Tap, { event: 'doubletap', taps: 2 }, ['tap']]
2727
]
2828
};
2929

@@ -43,14 +43,14 @@ export class HammerGesturesManager {
4343
public addEventListener(element: HTMLElement,
4444
eventName: string,
4545
eventHandler: (eventObj) => void,
46-
options: object = null): () => void {
46+
options: HammerOptions = null): () => void {
4747

4848
// Creating the manager bind events, must be done outside of angular
4949
return this._zone.runOutsideAngular(() => {
5050
let mc: HammerManager = this.getManagerForElement(element);
5151
if (mc === null) {
5252
// new Hammer is a shortcut for Manager with defaults
53-
mc = new Hammer(element, this.hammerOptions);
53+
mc = new Hammer(element, Object.assign(this.hammerOptions, options));
5454
this.addManagerForElement(element, mc);
5555
}
5656
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

@@ -482,7 +484,8 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy {
482484
public selection: IgxSelectionAPIService,
483485
public cdr: ChangeDetectorRef,
484486
private element: ElementRef,
485-
protected zone: NgZone) { }
487+
protected zone: NgZone,
488+
private touchManager: HammerGesturesManager) { }
486489

487490

488491
/**
@@ -510,6 +513,9 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy {
510513
this.nativeElement.addEventListener('focusout', this.focusOut);
511514
}
512515
});
516+
this.touchManager.addEventListener(this.nativeElement, 'doubletap', this.onDoubleClick, {
517+
cssProps: { } /* don't disable user-select, etc */
518+
} as HammerOptions);
513519
}
514520

515521
/**
@@ -529,6 +535,7 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy {
529535
this.nativeElement.removeEventListener('focusout', this.focusOut);
530536
}
531537
});
538+
this.touchManager.destroy();
532539
}
533540

534541
/**
@@ -674,7 +681,11 @@ export class IgxGridCellComponent implements OnInit, OnChanges, OnDestroy {
674681
* @internal
675682
*/
676683
@HostListener('dblclick', ['$event'])
677-
public onDoubleClick(event: MouseEvent) {
684+
public onDoubleClick = (event: MouseEvent| HammerInput) => {
685+
if (event.type === 'doubletap') {
686+
// prevent double-tap to zoom on iOS
687+
event.preventDefault();
688+
}
678689
if (this.editable && !this.editMode && !this.row.deleted) {
679690
this.crudService.begin(this);
680691
}

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, IgxNumberFilteringOperand } 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)