Skip to content
This repository was archived by the owner on Feb 22, 2018. It is now read-only.

feat: ng-swipe-left/ng-swipe-right directives #1403

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions lib/touch/module.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Touch related functionality for AngularDart apps.
*
* To use, install the TouchModule into your main module:
*
* var module = new Module()
* ..install(new TouchModule());
*
* Once the module is installed, you can use decorators such
* as ng-swipe-left or ng-swipe right
*/

library angular.touch;

import 'dart:html' as dom;
import 'package:di/di.dart';
import 'package:angular/core/annotation.dart';

part 'ng_swipe.dart';

class TouchModule extends Module {
TouchModule() {
bind(NgSwipeLeft, toValue: null);
bind(NgSwipeRight, toValue: null);
}
}

106 changes: 106 additions & 0 deletions lib/touch/ng_swipe.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
part of angular.touch;

/**
* Base class for Swipe Gesture. Decides whether a swipe is performed
* and gives the x, y direction of the swipe.
*/
abstract class _SwipeGesture {
static const int NO_SWIPE = -1;
static const int DOWN = 0;
static const int UP = 1;
static const int LEFT = 2;
static const int RIGHT = 3;

// Less than 100 pixels of move in each direction does not count.
static const int _POS_TOLERANCE = 100;
// If swipe lasts more than 1s, it is not a swipe.
static const int _TIME_TOLERANCE = 1000;

// Function to be called on swipe.
Function fn;

// Subclasses decide on swipe direction whether to call fn or not.
bool get shouldFire;

int _startX;
int _startY;
int _startTime;
int xDirection;
int yDirection;

_SwipeGesture(dom.Element target) {
target.onTouchStart.listen(_handleTouchStartEvent);
target.onTouchEnd.listen(_handleTouchEndEvent);
}

void handleTouchStart(int x, int y, int timestamp) {
// Reset values every time swipe starts
xDirection = NO_SWIPE;
yDirection = NO_SWIPE;
_startX = x;
_startY = y;
_startTime = timestamp;
}

void handleTouchEnd(int x, int y, int timestamp) {
int touchDuration = timestamp - _startTime;
if (touchDuration > _TIME_TOLERANCE) {
return;
}
if (y > _startY + _POS_TOLERANCE) {
yDirection = DOWN;
} else if (y < _startY - _POS_TOLERANCE) {
yDirection = UP;
}
if (x > _startX + _POS_TOLERANCE) {
xDirection = RIGHT;
} else if (x < _startX - _POS_TOLERANCE) {
xDirection = LEFT;
}
if (fn != null && shouldFire) {
fn();
}
}

void _handleTouchStartEvent(dom.TouchEvent ev) {
// Guaranteed to have at least one touch in changedTouches.
dom.Touch t = ev.changedTouches.first;
handleTouchStart(t.client.x, t.client.y, ev.timeStamp);
}

void _handleTouchEndEvent(dom.TouchEvent ev) {
// Guaranteed to have at least one touch in changedTouches.
dom.Touch t = ev.changedTouches.first;
handleTouchEnd(t.client.x, t.client.y, ev.timeStamp);
}
}

/**
* The `ng-swipe-right` directive allows execution of callbacks when user
* swipes her finger to the right.
* Also see [NgSwipeLeft].
*/
@Decorator(
selector: '[ng-swipe-right]',
map: const {'ng-swipe-right':'&fn'})
class NgSwipeRight extends _SwipeGesture {
NgSwipeRight(dom.Element target): super(target);

bool get shouldFire => xDirection == _SwipeGesture.RIGHT &&
yDirection == _SwipeGesture.NO_SWIPE;
}

/**
* The `ng-swipe-left` directive allows execution of callbacks when user
* swipes his finger to the left.
* Also see [NgSwipeRight].
*/
@Decorator(
selector: '[ng-swipe-left]',
map: const {'ng-swipe-left':'&fn'})
class NgSwipeLeft extends _SwipeGesture {
NgSwipeLeft(dom.Element target): super(target);

bool get shouldFire => xDirection == _SwipeGesture.LEFT &&
yDirection == _SwipeGesture.NO_SWIPE;
}
1 change: 1 addition & 0 deletions test/_specs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export 'package:angular/directive/module.dart';
export 'package:angular/formatter/module.dart';
export 'package:angular/routing/module.dart';
export 'package:angular/animate/module.dart';
export 'package:angular/touch/module.dart';
export 'package:angular/mock/module.dart';
export 'package:perf_api/perf_api.dart';

Expand Down
17 changes: 17 additions & 0 deletions test/angular_spec.dart
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,23 @@ main() {
assertSymbolNamesAreOk(ALLOWED_NAMES, libraryInfo);

});

it('should not export unknown symbols from touch', () {
LibraryInfo libraryInfo;
try {
libraryInfo = getSymbolsFromLibrary("angular.touch");
} on UnimplementedError catch (e) {
return; // Not implemented, quietly skip.
}

var ALLOWED_NAMES = [
"angular.touch.NgSwipeLeft",
"angular.touch.NgSwipeRight",
"angular.touch.TouchModule"
];
assertSymbolNamesAreOk(ALLOWED_NAMES, libraryInfo);

});

it('should not export unknown symbols from angular', () {
// Test is failing? Add new symbols to the "ALLOWED_NAMES" list below.
Expand Down
71 changes: 71 additions & 0 deletions test/touch/ng_swipe_spec.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
library ng_swipe_spec;

import '../_specs.dart';

/**
* Unfortunately, it is not possible to test swipe using events since
* TouchEvent cannot be constructed using fake dom.Touch elements.
* See: dartbug.com/8314
* TODO(8314): Once this is fixed, should update the tests.
*/
void main() {
describe('ng-swipe-right', () {
NgSwipeRight swipe = new NgSwipeRight(new DivElement());

it('should not fire when distance is not enough', () {
swipe.handleTouchStart(10, 10, 0);
swipe.handleTouchEnd(15, 15, 1);
expect(swipe.shouldFire).toBeFalse();
});

it('should fire on swipe to the right', () {
swipe.handleTouchStart(10, 10, 0);
swipe.handleTouchEnd(130, 15, 1);
expect(swipe.shouldFire).toBeTrue();
});

it('should not fire on swipe to the left', () {
swipe.handleTouchStart(130, 10, 0);
swipe.handleTouchEnd(10, 15, 1);
expect(swipe.shouldFire).toBeFalse();
});

it('should not fire on slow swipe', () {
swipe.handleTouchStart(10, 10, 0);
// 2 seconds later
swipe.handleTouchEnd(130, 15, 2000);
expect(swipe.shouldFire).toBeFalse();
});


});

describe('ng-swipe-left', () {
NgSwipeLeft swipe = new NgSwipeLeft(new DivElement());

it('should not fire when distance is not enough', () {
swipe.handleTouchStart(10, 10, 0);
swipe.handleTouchEnd(15, 15, 1);
expect(swipe.shouldFire).toBeFalse();
});

it('should not fire on swipe to the right', () {
swipe.handleTouchStart(10, 10, 0);
swipe.handleTouchEnd(130, 15, 1);
expect(swipe.shouldFire).toBeFalse();
});

it('should fire on swipe to the left', () {
swipe.handleTouchStart(130, 10, 0);
swipe.handleTouchEnd(10, 15, 1);
expect(swipe.shouldFire).toBeTrue();
});

it('should not fire on swipe on slow swipe', () {
swipe.handleTouchStart(130, 10, 0);
// 2 seconds later
swipe.handleTouchEnd(10, 15, 2000);
expect(swipe.shouldFire).toBeFalse();
});
});
}