Skip to content
This repository has been archived by the owner on Jan 12, 2021. It is now read-only.

Rewrite suggestion #49

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
Binary file added .DS_Store
Binary file not shown.
Binary file added src/.DS_Store
Binary file not shown.
Binary file added src/components/.DS_Store
Binary file not shown.
Binary file added src/components/ng2-swipe-cards/.DS_Store
Binary file not shown.
Binary file added src/components/swipe-cards/.DS_Store
Binary file not shown.
Binary file added src/components/swipe-cards/shared/.DS_Store
Binary file not shown.
Binary file not shown.
62 changes: 62 additions & 0 deletions src/components/swipe-cards/shared/components/card.component.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
.card-wrapper {
width: 100%;
height: 100%;
}

:host {
transition: transform 1s ease;
position: absolute;
border-radius: 2px;
border: 1px solid white;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3) !important;
transition: transform 0.2s ease;
background-color: white;
touch-action: none !important;
}

:host(.card-heap) {
transition: transform 1s ease;
display: block;
position: absolute;
background-color: white;
box-shadow: 0 0 0 rgba(0, 0, 0, 0) !important;
transform: perspective(400px) translate3d(0, 30px, -30px);
visibility: hidden;
}

:host(.card-heap):nth-child(1) {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3) !important;
z-index:3;
visibility: visible;
transform: perspective(400px) translate3d(0, 0px, 0px);
}

:host(.card-heap):nth-child(2) {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3) !important;
z-index:2;
visibility: visible;
transform: perspective(400px) translate3d(0, 30px, -30px);
}

:host(.card-heap):nth-child(3) {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3) !important;
z-index:1;
visibility: visible;
transform: perspective(400px) translate3d(0, 60px, -60px);
}

:host .card-overlay {
transform: translateZ(0);
opacity: 0;
border-radius: 2px;
position: absolute;
width: 100%;
height: 10px;
top: 0;
left: 0;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
color: white;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<div class="card-wrapper">
<ng-content></ng-content>
<div #overlay class="card-overlay"></div>
</div>
255 changes: 255 additions & 0 deletions src/components/swipe-cards/shared/components/card.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
import {
AfterViewChecked,
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef,
EventEmitter,
HostBinding,
HostListener,
Input,
OnDestroy,
OnInit,
Output,
Renderer2,
ViewChild
} from '@angular/core';

import {Subscription} from 'rxjs/Subscription';
import 'rxjs/add/operator/filter';

import {SwipeCardsApi} from '@components/swipe-cards/shared/services/swipe-cards-api.service';

@Component({
moduleId: module.id,
selector: 'card',
templateUrl: './card.component.html',
styleUrls: ['./card.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush
})

export class CardComponent implements AfterViewChecked, OnInit, OnDestroy {
@Input() card: any;
@Input() cardElement: any;
@Input() position: number;
@Input() beforeSwipeAction: (event: any) => boolean;

@Input() set config(config) {

if (config) {
this._config = config;
}
}

@Input() set orientation(value: string) {
this._orientation = value || 'xy';
}

get config() {
return this._config;
}

get orientation() {
return this._orientation;
}

@Output() onLike: EventEmitter<any> = new EventEmitter<any>();
@Output() onDislike: EventEmitter<any> = new EventEmitter<any>();
@Output() onRelease: EventEmitter<any> = new EventEmitter<any>();
@Output() onSwipe: EventEmitter<any> = new EventEmitter<any>();
@Output() onAbort: EventEmitter<any> = new EventEmitter<any>();
@Output() onDisallowed: EventEmitter<any> = new EventEmitter<any>();
@Output() onSpecial: EventEmitter<any> = new EventEmitter<any>();
@Output() removeCard: EventEmitter<any> = new EventEmitter<any>();
@ViewChild('overlay') overlay: ElementRef;

public releaseRadius: any;
public released: Boolean = false;
public element: HTMLElement;

public direction: any = {
x: 0,
y: 0
};

private _config: any;
private _orientation = 'xy';

private swipeActionSubscription: Subscription;

@HostBinding('class.card-heap') isCardHeapClass = true;

@HostListener('pan', ['$event'])
onPan(event: any) {

if (!this.config.fixed && !this.released && this.position === 0) {
this.onSwipeCb(event);
}
}

@HostListener('panend', ['$event'])
onPanEnd(event: any) {

if (!this.config.fixed && !this.released && this.position === 0) {

if ((this._orientation === 'x' && (this.releaseRadius.x < event.deltaX || this.releaseRadius.x * -1 > event.deltaX)) || (this._orientation === 'y' && (this.releaseRadius.y < event.deltaY || this.releaseRadius.y * -1 > event.deltaY)) || ((this.releaseRadius.x < event.deltaX || this.releaseRadius.x * -1 > event.deltaX) || (this.releaseRadius.y < event.deltaY || this.releaseRadius.y * -1 > event.deltaY))) {
this.onReleaseCb(event);
}
else {
this.onAbortCb(event);
}
}
}

constructor(
private cd: ChangeDetectorRef,
protected el: ElementRef,
private renderer: Renderer2,
private swipeCardsApi: SwipeCardsApi
) {}

ngOnInit() {

this.element = this.el.nativeElement;

this.swipeActionSubscription = this.swipeCardsApi.swipeAction$
.filter(() => this.position === 0)
.subscribe((options: any) => {

if (this.beforeSwipeAction) {

const canProceedWithAction: boolean = this.beforeSwipeAction({action: options.action, card: this.card});

if (canProceedWithAction) {
this.onAction(options.action, options.direction);
}
else {
this.onDisallowed.emit({action: options.action, card: this.card});
}
}
else {
this.onAction(options.action, options.direction);
}
}
);
}

ngAfterViewChecked() {

if (this.element.parentElement) {

const height = this.element.parentElement.clientHeight;
const width = this.element.parentElement.clientWidth;

this.renderer.setStyle(this.element, 'height', height + 'px');
this.renderer.setStyle(this.element, 'width', width + 'px');

this.releaseRadius = {
x: width / 4,
y: height / 4
};
}
}

ngOnDestroy() {
this.swipeActionSubscription.unsubscribe();
}

translate(params: any) {

if (!this.config.fixed) {
this.renderer.setStyle(this.element, 'transition', 'transform ' + (params.time || 0) + 's ease');
this.renderer.setStyle(this.element, 'webkitTransform', `translate3d(${(params.x && (!this._orientation || this._orientation.indexOf('x') !== -1) ? (params.x) : 0)}px, ${(params.y && (!this._orientation || this._orientation.indexOf('y') !== -1) ? (params.y) : 0)}px, 0) rotate(${params.rotate || 0}deg)`);
}
}

onSwipeCb(event: any) {

const like = (this.orientation === 'y' && event.deltaY < 0) || (this.orientation !== 'y' && event.deltaX > 0);
const opacity = (event.distance < 0 ? event.distance * -1 : event.distance) * 0.5 / this.element.offsetWidth;

if (!!this.config.overlay) {

this.renderer.setStyle(this.overlay.nativeElement, 'transition', 'opacity 0s ease');
this.renderer.setStyle(this.overlay.nativeElement, 'opacity', opacity.toString());
this.renderer.setStyle(this.overlay.nativeElement, 'background-color', this.config.overlay[like ? 'like' : 'dislike'].backgroundColor);
}

this.translate({
x: event.deltaX,
y: event.deltaY,
rotate: ((event.deltaX * 20) / this.element.clientWidth)
});

this.onSwipe.emit(event);
}

onAbortCb(event: any): void {

if (!!this.config.overlay) {
this.renderer.setStyle(this.overlay.nativeElement, 'transition', 'opacity 0.2s ease');
this.renderer.setStyle(this.overlay.nativeElement, 'opacity', '0');
}

this.translate({
x: 0,
y: 0,
rotate: 0,
time: 0.2
});

this.onAbort.emit(event);
}

onReleaseCb(event: any): void {

this.released = true;

const like = (this.orientation === 'y' && event.deltaY < 0) || (this.orientation !== 'y' && event.deltaX > 0);

if (this.beforeSwipeAction) {

const canProceedWithAction: boolean = this.beforeSwipeAction({action: like ? 'like' : 'dislike', card: this.card});

if (canProceedWithAction) {
this.onAction(like ? 'like' : 'dislike', like ? 'right' : 'left');
}
else {
this.onDisallowed.emit({action: like ? 'like' : 'dislike', card: this.card});
}
}
else {
this.onAction(like ? 'like' : 'dislike', like ? 'right' : 'left');
}

this.onRelease.emit(event);
}

onAction(action: string, direction: string): void {

const el = this.element;
const x = (el.offsetWidth + el.clientWidth) * (direction === 'right' ? 1 : -1);
const y = (el.offsetHeight + el.clientHeight) * (direction === 'right' ? -1 : 1);

this.translate({
x: x,
y: y,
rotate: (x * 20) / el.clientWidth,
time: 0.8
});

this.renderer.setStyle(this.overlay.nativeElement, 'transition', 'opacity 0.4s ease');
this.renderer.setStyle(this.overlay.nativeElement, 'opacity', '0.5');
this.renderer.setStyle(this.overlay.nativeElement, 'background-color', this.config.overlay[action].backgroundColor);

switch (action) {
case 'like': this.onLike.emit({like: true, card: this.card}); break;
case 'dislike': this.onDislike.emit({like: false, card: this.card}); break;
case 'special': this.onSpecial.emit({special: true, card: this.card}); break;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the use of the 'special' action? It seems to have no behaviors in the components apart from sending a callback with the card element.

Copy link
Collaborator Author

@chrillewoodz chrillewoodz May 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's supposed to be like a "super like" or whatever you want it to be. I've got some ideas how we can make this better, by instead of having like/dislike etc we have left/right/top/bottom swipes instead. And then you can decide on your own what you want each direction to do. This would make it really easy to customise the behaviour.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now it only fires from a button using a directive. That's why you can't see any behaviour attached to it, it basically just changes the overlay color.

Copy link
Owner

@Viczei Viczei May 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see what you mean, so the special is just an example and we can add for example left, top, ... etc actions
It's indeed more logical than a like or dislike which would not always be the use of a tinder card even if the most common

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the plan is to make left/right/top/bottom actions, wouldn't it be better to make action and direction the same argument for the function?
And treat "like" as a "right" and "dislike" as a "left", in order to prevent regressions for current behaviors.

I don't really see the advantages of the "special", if it's needed to change the overlay component, i think it's already possible through the config input.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if we specify exactly what we want to do it'll be easier to understand. The action should indeed be left, right etc. instead of like/dislike. I don't think we need to think a lot about regression since the repo isn't that big yet. Now we have the chance to make it right :)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes i think too that we should specify a bit all this. I also had plans for the future by adding a drag and drop system to cards (tinder cards beeing a drag and drop system)
I did begin a work on the branch card-grid

}

setTimeout(() => {
this.removeCard.emit(this.card);
}, 200);
}
}
18 changes: 18 additions & 0 deletions src/components/swipe-cards/shared/components/card.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';

import {CardComponent} from './card.component';

@NgModule({
imports: [
CommonModule
],
exports: [
CardComponent
],
declarations: [
CardComponent
]
})

export class CardModule {}
25 changes: 25 additions & 0 deletions src/components/swipe-cards/shared/directives/action.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {Directive, Input, HostListener} from '@angular/core';

import {SwipeCardsApi} from '@components/swipe-cards/shared/services/swipe-cards-api.service';

@Directive({
selector: '[swipeCardsAction]'
})

export class ActionDirective {
@Input() action: string;
@Input() direction: string;
@Input() beforeSwipeAction: () => boolean;

constructor(private swipeCardsApi: SwipeCardsApi) {}

@HostListener('click')
swipeCard(): void {

this.swipeCardsApi.swipe({
action: this.action,
beforeSwipeAction: this.beforeSwipeAction,
direction: this.direction
});
}
}
Loading