Skip to content

Instantly share code, notes, and snippets.

@federicofazzeri
Last active March 4, 2021 18:20
Show Gist options
  • Save federicofazzeri/fa2776915d42b84b0d4d487858cd65d7 to your computer and use it in GitHub Desktop.
Save federicofazzeri/fa2776915d42b84b0d4d487858cd65d7 to your computer and use it in GitHub Desktop.
Angular 2 state-less directive to add cross device left/right swipe gesture to a UI list
import { Directive, HostListener, ElementRef, Output, Input, EventEmitter, AfterContentInit } from '@angular/core';
import { Observable } from 'rxjs/Rx'
import 'rxjs/add/observable/fromEvent';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/takeUntil';
import 'rxjs/add/operator/concatMap';
import { Subject } from 'rxjs/Subject';
@Directive({
selector: '[swipeEvent]'
})
export class SwipeEventDirective implements AfterContentInit {
@Input() swipeWidth;
@Output() onSwipeEvent = new EventEmitter();
constructor(private el: ElementRef) { }
ngAfterContentInit() {
this.moveAndDelete('mousedown', 'mousemove', 'mouseup', 'mouseout');
this.moveAndDelete('touchstart', 'touchmove', 'touchend', 'touchcancel');
}
private moveEle = ele => px => ele.style.transform = 'translateX('+ px +'px)';
private resetPos = ele => () => ele.style.transform = 'translateX(0px)';
private emitEvent = px => {
if(Math.abs(px) > this.swipeWidth) {
this.onSwipeEvent.emit({right: px > 0});
return true;
}
return false;
}
private getX = ev => {
if(ev.screenX) {
return ev.screenX;
} else if(ev.touches && ev.touches[0].screenX) {
return ev.touches[0].screenX;
}
return false;
}
private moveAndDelete = (evStart:string, evMove:string, evEnd:string, evCancel:string) => {
const start: Observable<any> = Observable.fromEvent(this.el.nativeElement, evStart);
const move: Observable<any> = Observable.fromEvent(this.el.nativeElement, evMove);
const end: Observable<any> = Observable.fromEvent(this.el.nativeElement, evEnd);
const cancel: Observable<any> = Observable.fromEvent(this.el.nativeElement, evCancel);
const moveEle: Function = this.moveEle(this.el.nativeElement.querySelector('.container'));
const resetPos = this.resetPos(this.el.nativeElement.querySelector('.container'));
const eventEmitted = new Subject<string>();
start
.map( ev => this.getX(ev) )
.concatMap( start =>
move
.map(ev => {
const delta: number = this.getX(ev) - start;
moveEle(delta);
if(this.emitEvent(delta)) {
eventEmitted.next();
resetPos();
}
})
.takeUntil(cancel)
.takeUntil(end)
.takeUntil(eventEmitted)
)
.subscribe();
cancel.subscribe(resetPos)
end.subscribe(resetPos)
}
}
import { Component } from '@angular/core';
@Component({
template: `
<div>
<h2>swipe to delete demo</h2>
<ul>
<li *ngFor="let item of myList" swipeEvent [swipeWidth]="200" (onSwipeEvent)="swiped($event, item)">
<div class="container">
<div class="text"><span>{{item.text}}</span></div>
</div>
</li>
</ul>
</div>`,
styles: [`
div{width:300px}
ul, li {
margin: 0;
padding: 0;
}
li {
list-style: none;
border-radius: 5px;
background-color: red;
margin-bottom: 10px;
}
.container {
border-radius: 4px;
padding:10px;
display: flex;
background-color: #F1F1F1;
}
.text {
flex: 0 0 90%;
display: flex;
align-items: center;
}
button {
background-color: #CCC;
border-radius: 50%;
height: 30px;
width: 30px;
border: none;
margin-right: 10px;
}
`]
})
export class SwipeListComponentDemo {
myList = [
{ id: 1, text: 'one'},
{ id: 2, text: 'two'},
{ id: 3, text: 'three'}
]
constructor() { }
swiped(direction, item){
console.log(direction, item);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment