Skip to content
Permalink
Browse files

fix(Mobile): Allow either touch and mouse events

Many features were broken on mobile devices due to us only binding to
mouse-based events. Switching to touch-events, where supported, should fix
this.

BREAKING CHANGE: On mobile devices the user will have to long-click to
edit a cell instead of double-clicking
  • Loading branch information
c0bra committed Dec 10, 2014
1 parent 2dc0275 commit 654e0ce83f1ebd824341ab3d63b6d38495a67b80
@@ -369,8 +369,10 @@
*
*/
module.directive('uiGridCell',
['$compile', '$injector', 'uiGridConstants', 'uiGridEditConstants', 'gridUtil', '$parse', 'uiGridEditService',
function ($compile, $injector, uiGridConstants, uiGridEditConstants, gridUtil, $parse, uiGridEditService) {
['$compile', '$injector', '$timeout', 'uiGridConstants', 'uiGridEditConstants', 'gridUtil', '$parse', 'uiGridEditService',
function ($compile, $injector, $timeout, uiGridConstants, uiGridEditConstants, gridUtil, $parse, uiGridEditService) {
var touchstartTimeout = 500;

return {
priority: -100, // run after default uiGridCell directive
restrict: 'A',
@@ -386,6 +388,7 @@
var inEdit = false;
var isFocusedBeforeEdit = false;
var cellModel;
var cancelTouchstartTimeout;

registerBeginEditEvents();

@@ -395,6 +398,37 @@
if ($scope.col.colDef.enableCellEditOnFocus) {
$elm.find('div').on('focus', beginEditFocus);
}

// Add touchstart handling. If the users starts a touch and it doesn't end after X milliseconds, then start the edit
$elm.on('touchstart', touchStart);
}

function touchStart(event) {
// jQuery masks events
if (typeof(event.originalEvent) !== 'undefined' && event.originalEvent !== undefined) {
event = event.originalEvent;
}

// Bind touchend handler
$elm.on('touchend', touchEnd);

// Start a timeout
cancelTouchstartTimeout = $timeout(function() { }, touchstartTimeout);

// Timeout's done! Start the edit
cancelTouchstartTimeout.then(function () {
// Use setTimeout to start the edit because beginEdit expects to be outside of $digest
setTimeout(beginEdit, 0);

// Undbind the touchend handler, we don't need it anymore
$elm.off('touchend', touchEnd);
});
}

// Cancel any touchstart timeout
function touchEnd(event) {
$timeout.cancel(cancelTouchstartTimeout);
$elm.off('touchend', touchEnd);
}

function cancelBeginEditEvents() {
@@ -403,6 +437,7 @@
if ($scope.col.colDef.enableCellEditOnFocus) {
$elm.find('div').off('focus', beginEditFocus);
}
$elm.off('touchstart', touchStart);
}

function beginEditFocus(evt) {
@@ -634,8 +669,8 @@
*
*/
module.directive('uiGridEditor',
['uiGridConstants', 'uiGridEditConstants',
function (uiGridConstants, uiGridEditConstants) {
['gridUtil', 'uiGridConstants', 'uiGridEditConstants',
function (gridUtil, uiGridConstants, uiGridEditConstants) {
return {
scope: true,
require: ['?^uiGrid', '?^uiGridRenderContainer'],
@@ -263,6 +263,19 @@
module.directive('uiGridColumnResizer', ['$document', 'gridUtil', 'uiGridConstants', 'columnBounds', 'uiGridResizeColumnsService', function ($document, gridUtil, uiGridConstants, columnBounds, uiGridResizeColumnsService) {
var resizeOverlay = angular.element('<div class="ui-grid-resize-overlay"></div>');

var downEvent, upEvent, moveEvent;

if (gridUtil.isTouchEnabled()) {
downEvent = 'touchstart';
upEvent = 'touchend';
moveEvent = 'touchmove';
}
else {
downEvent = 'mousedown';
upEvent = 'mouseup';
moveEvent = 'mousemove';
}

var resizer = {
priority: 0,
scope: {
@@ -321,7 +334,7 @@
if (event.originalEvent) { event = event.originalEvent; }
event.preventDefault();

x = event.clientX - gridLeft;
x = (event.targetTouches ? event.targetTouches[0] : event).clientX - gridLeft;

if (x < 0) { x = 0; }
else if (x > uiGridCtrl.grid.gridWidth) { x = uiGridCtrl.grid.gridWidth; }
@@ -379,12 +392,12 @@
resizeOverlay.remove();

// Resize the column
x = event.clientX - gridLeft;
x = (event.changedTouches ? event.changedTouches[0] : event).clientX - gridLeft;
var xDiff = x - startX;

if (xDiff === 0) {
$document.off('mouseup', mouseup);
$document.off('mousemove', mousemove);
$document.off(upEvent, mouseup);
$document.off(moveEvent, mousemove);
return;
}

@@ -431,11 +444,11 @@

uiGridResizeColumnsService.fireColumnSizeChanged(uiGridCtrl.grid, col.colDef, xDiff);

$document.off('mouseup', mouseup);
$document.off('mousemove', mousemove);
$document.off(upEvent, mouseup);
$document.off(moveEvent, mousemove);
}

$elm.on('mousedown', function(event, args) {
$elm.on(downEvent, function(event, args) {
if (event.originalEvent) { event = event.originalEvent; }
event.stopPropagation();

@@ -444,7 +457,7 @@
gridLeft = uiGridCtrl.grid.element[0].getBoundingClientRect().left;

// Get the starting X position, which is the X coordinate of the click minus the grid's offset
startX = event.clientX - gridLeft;
startX = (event.targetTouches ? event.targetTouches[0] : event).clientX - gridLeft;

// Append the resizer overlay
uiGridCtrl.grid.element.append(resizeOverlay);
@@ -453,8 +466,8 @@
resizeOverlay.css({ left: startX });

// Add handlers for mouse move and up events
$document.on('mouseup', mouseup);
$document.on('mousemove', mousemove);
$document.on(upEvent, mouseup);
$document.on(moveEvent, mousemove);
});

// On doubleclick, resize to fit all rendered cells
@@ -539,10 +552,10 @@
});

$elm.on('$destroy', function() {
$elm.off('mousedown');
$elm.off(downEvent);
$elm.off('dblclick');
$document.off('mousemove', mousemove);
$document.off('mouseup', mouseup);
$document.off(moveEvent, mousemove);
$document.off(upEvent, mouseup);
});
}
};
@@ -1,6 +1,8 @@
describe('ui.grid.resizeColumns', function () {
ddescribe('ui.grid.resizeColumns', function () {
var grid, gridUtil, gridScope, $scope, $compile, recompile, uiGridConstants;

var downEvent, upEvent, moveEvent;

var data = [
{ "name": "Ethel Price", "gender": "female", "company": "Enersol" },
{ "name": "Claudine Neal", "gender": "female", "company": "Sealoud" },
@@ -17,6 +19,17 @@ describe('ui.grid.resizeColumns', function () {
uiGridConstants = _uiGridConstants_;
gridUtil = _gridUtil_;

if (gridUtil.isTouchEnabled()) {
downEvent = 'touchstart';
upEvent = 'touchend';
moveEvent = 'touchmove';
}
else {
downEvent = 'mousedown';
upEvent = 'mouseup';
moveEvent = 'mousemove';
}

$scope.gridOpts = {
enableColumnResizing: true,
data: data
@@ -133,7 +146,7 @@ describe('ui.grid.resizeColumns', function () {
it('should cause the column separator overlay to be added', function () {
var firstResizer = $(grid).find('[ui-grid-column-resizer]').first();

firstResizer.trigger('mousedown');
firstResizer.trigger(downEvent);
$scope.$digest();

var overlay = $(grid).find('.ui-grid-resize-overlay');
@@ -156,15 +169,15 @@ describe('ui.grid.resizeColumns', function () {

initialX = firstResizer.position().left;

$(firstResizer).simulate('mousedown', { clientX: initialX });
$(firstResizer).simulate(downEvent, { clientX: initialX });
$scope.$digest();

// Get the overlay
overlay = $(grid).find('.ui-grid-resize-overlay');
initialOverlayX = $(overlay).position().left;

xDiff = 100;
$(document).simulate('mousemove', { clientX: initialX + xDiff });
$(document).simulate(moveEvent, { clientX: initialX + xDiff });
$scope.$digest();
});

@@ -186,7 +199,7 @@ describe('ui.grid.resizeColumns', function () {

describe('then releasing the mouse', function () {
beforeEach(function () {
$(document).simulate('mouseup', { clientX: initialX + xDiff });
$(document).simulate(upEvent, { clientX: initialX + xDiff });
$scope.$digest();
});

@@ -243,10 +256,10 @@ describe('ui.grid.resizeColumns', function () {
var firstResizer = $(grid).find('[ui-grid-column-resizer]').first();
initialX = firstResizer.position().left;

$(firstResizer).simulate('mousedown', { clientX: initialX });
$(firstResizer).simulate(downEvent, { clientX: initialX });
$scope.$digest();

$(document).simulate('mouseup', { clientX: initialX - minWidth });
$(document).simulate(upEvent, { clientX: initialX - minWidth });
$scope.$digest();
});

@@ -297,10 +310,10 @@ describe('ui.grid.resizeColumns', function () {
var firstResizer = $(grid).find('[ui-grid-column-resizer]').first();
initialX = firstResizer.position().left;

$(firstResizer).simulate('mousedown', { clientX: initialX });
$(firstResizer).simulate(downEvent, { clientX: initialX });
$scope.$digest();

$(document).simulate('mouseup', { clientX: initialX + maxWidth });
$(document).simulate(upEvent, { clientX: initialX + maxWidth });
$scope.$digest();
});

@@ -136,13 +136,15 @@
*
*/

if ( $scope.sortable || $scope.colMenu ){
if ($scope.sortable || $scope.colMenu) {
// Long-click (for mobile)
var cancelMousedownTimeout;
var mousedownStartTime = 0;

var downEvent = gridUtil.isTouchEnabled() ? 'touchstart' : 'mousedown';
$contentsElm.on(downEvent, function(event) {
gridUtil.logDebug('mouse event', event.type);

event.stopPropagation();

if (typeof(event.originalEvent) !== 'undefined' && event.originalEvent !== undefined) {
@@ -203,6 +205,8 @@
if ($scope.sortable) {
var clickEvent = gridUtil.isTouchEnabled() ? 'touchend' : 'click';
$contentsElm.on(clickEvent, function(event) {
gridUtil.logDebug('mouse event 2', event.type);

event.stopPropagation();

$timeout.cancel(cancelMousedownTimeout);
@@ -211,10 +215,12 @@
var mousedownTime = mousedownEndTime - mousedownStartTime;

if (mousedownTime > mousedownTimeout) {
gridUtil.logDebug('long click');
// long click, handled above with mousedown
}
else {
// short click
gridUtil.logDebug('short click');
handleClick(event);
}
});

0 comments on commit 654e0ce

Please sign in to comment.
You can’t perform that action at this time.