├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── package.json
├── src
├── index.ts
├── swing-card-component.ts
├── swing-stack-component.ts
├── swing.module.ts
└── swing.ts
├── tsconfig.json
├── tslint.json
└── typings.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 | typings
4 | aot
5 | dist
6 | .idea/
7 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | src
3 | typings
4 | tsconfig.json
5 | tslint.json
6 | typings.json
7 | aot
8 | .idea/
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Kapil Sachdeva
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # angular2-swing
2 |
3 | # Build package
4 |
5 | `npm run build`
6 |
7 | dont forget `typings install`
8 |
9 | # Install
10 | npm install angular2-swing --save
11 |
12 | # Usage
13 | ```JavaScript
14 | import {Component, ViewChild, ViewChildren, QueryList} from '@angular/core';
15 |
16 | import {
17 | Direction,
18 | StackConfig,
19 | Stack,
20 | Card,
21 | ThrowEvent,
22 | DragEvent,
23 | SwingStackComponent,
24 | SwingCardComponent} from 'angular2-swing';
25 |
26 | @Component({
27 | selector: 'app',
28 | template: `
29 |
34 |
35 |
Drag the playing cards out of the stack and let go. Dragging them
36 | beyond the desk will throw them out of the stack. If you drag too
37 | little and let go, the cards will spring back into place. You can
38 | throw cards back into the stack after you have thrown them out.
39 |
Open the
40 | Console to view the associated events.
41 |
Demonstration of
42 | https://github.com/ksachdeva/angular2-swing implementation.
43 |
44 |
45 | `
46 | })
47 | export class AppComponent {
48 |
49 | @ViewChild('myswing1') swingStack: SwingStackComponent;
50 | @ViewChildren('mycards1') swingCards: QueryList;
51 |
52 | cards: Array;
53 | stackConfig: StackConfig;
54 |
55 | constructor() {
56 |
57 | this.stackConfig = {
58 | // Default setting only allows UP, LEFT and RIGHT so you can override this as below
59 | allowedDirections: [Direction.UP, Direction.DOWN, Direction.LEFT, Direction.RIGHT],
60 | // Now need to send offsetX and offsetY with element instead of just offset
61 | throwOutConfidence: (offsetX, offsetY, element) => {
62 | return Math.min(Math.max(Math.abs(offsetX) / (element.offsetWidth / 1.7), Math.abs(offsetY) / (element.offsetHeight / 2)), 1);
63 | },
64 | throwOutDistance: (d) => {
65 | return 800;
66 | }
67 | }
68 |
69 | this.cards = [
70 | { name: 'clubs', symbol: '♣' },
71 | { name: 'diamonds', symbol: '♦' },
72 | { name: 'spades', symbol: '♠' }
73 | ];
74 | }
75 |
76 | ngAfterViewInit() {
77 | // ViewChild & ViewChildren are only available
78 | // in this function
79 |
80 | console.log(this.swingStack); // this is the stack
81 | console.log(this.swingCards); // this is a list of cards
82 |
83 | // we can get the underlying stack
84 | // which has methods - createCard, destroyCard, getCard etc
85 | console.log(this.swingStack.stack);
86 |
87 | // and the cards
88 | // every card has methods - destroy, throwIn, throwOut etc
89 | this.swingCards.forEach((c) => console.log(c.getCard()));
90 |
91 | // this is how you can manually hook up to the
92 | // events instead of providing the event method in the template
93 | this.swingStack.throwoutleft.subscribe(
94 | (event: ThrowEvent) => console.log('Manual hook: ', event));
95 |
96 | this.swingStack.dragstart.subscribe((event: DragEvent) => console.log(event));
97 |
98 | this.swingStack.dragmove.subscribe((event: DragEvent) => console.log(event));
99 | }
100 |
101 | // This method is called by hooking up the event
102 | // on the HTML element - see the template above
103 | onThrowOut(event: ThrowEvent) {
104 | console.log('Hook from the template', event.throwDirection);
105 | }
106 | }
107 |
108 | ```
109 |
110 | See [angular2-swing-example](https://github.com/ksachdeva/angular2-swing-example) repository for the full example
111 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular2-swing",
3 | "version": "0.12.0",
4 | "description": "Angular 2 Component for Swing (Tinder style cards)",
5 | "main": "dist/index.js",
6 | "typings": "dist/index.d.ts",
7 | "scripts": {
8 | "build-app": "./node_modules/.bin/ngc",
9 | "clean": "rm -rf ./dist && rm -rf ./aot",
10 | "build": "npm run clean && npm run build-app"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/ksachdeva/angular2-swing.git"
15 | },
16 | "author": "Kapil Sachdeva ",
17 | "license": "MIT",
18 | "bugs": {
19 | "url": "https://github.com/ksachdeva/angular2-swing/issues"
20 | },
21 | "homepage": "https://github.com/ksachdeva/angular2-swing#readme",
22 | "dependencies": {
23 | "swing": "^3.1.1"
24 | },
25 | "devDependencies": {
26 | "@angular/common": "^4.0.0",
27 | "@angular/compiler": "^4.0.0",
28 | "@angular/core": "^4.0.0",
29 | "@angular/cli": "1.0.0",
30 | "@angular/compiler-cli": "^4.0.0",
31 | "core-js": "^2.4.1",
32 | "reflect-metadata": "^0.1.3",
33 | "rxjs": "5.0.0-beta.12",
34 | "zone.js": "^0.8.4"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export { SwingStackComponent } from './swing-stack-component';
2 | export { SwingCardComponent } from './swing-card-component';
3 | export {
4 | ThrowEvent,
5 | DragEvent,
6 | ThrowEventName,
7 | DragEventName,
8 | Card,
9 | Stack,
10 | StackConfig,
11 | Direction } from './swing';
12 | export { SwingModule } from './swing.module';
13 |
--------------------------------------------------------------------------------
/src/swing-card-component.ts:
--------------------------------------------------------------------------------
1 | import {Component, ElementRef, Host, Input} from '@angular/core';
2 | import {SwingStackComponent} from './swing-stack-component';
3 | import {Card} from './swing';
4 |
5 | @Component({
6 | selector: '[swing-card]',
7 | template: `
8 |
9 | `
10 | })
11 | export class SwingCardComponent {
12 | @Input() prepend: boolean = false;
13 |
14 | card: Card;
15 |
16 | constructor(
17 | private elmentRef: ElementRef,
18 | private swingStack: SwingStackComponent) {
19 | }
20 |
21 | ngOnInit() {
22 | this.swingStack.addCard(this, this.prepend);
23 | }
24 |
25 | getElementRef() {
26 | return this.elmentRef;
27 | }
28 |
29 | getNativeElement() {
30 | return this.elmentRef.nativeElement;
31 | }
32 |
33 | getCard(): Card {
34 | return this.swingStack.stack.getCard(this.getNativeElement());
35 | }
36 |
37 | destroyCard() {
38 | this.swingStack.cards = this.swingStack.cards.filter(swingCardComponent => swingCardComponent !== this);
39 | let card = this.swingStack.stack.getCard(this.getNativeElement());
40 | this.swingStack.stack.destroyCard(card);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/swing-stack-component.ts:
--------------------------------------------------------------------------------
1 | declare var require: any;
2 |
3 | import {Component, Input, ContentChildren, QueryList,
4 | AfterContentInit, EventEmitter } from '@angular/core';
5 |
6 | import { Direction, ThrowEvent, DragEvent, Stack, Card, StackConfig } from './swing';
7 | import {SwingCardComponent} from './swing-card-component';
8 |
9 | const Swing = require('swing');
10 |
11 | @Component({
12 | selector: '[swing-stack]',
13 | template: `
14 |
15 | `,
16 | outputs: [
17 | 'throwout',
18 | 'throwoutend',
19 | 'throwoutleft',
20 | 'throwoutright',
21 | 'throwoutup',
22 | 'throwoutdown',
23 | 'throwin',
24 | 'throwinend',
25 | 'dragstart',
26 | 'dragmove',
27 | 'dragend',
28 | ]
29 | })
30 | export class SwingStackComponent implements AfterContentInit {
31 |
32 | @Input() stackConfig: StackConfig;
33 |
34 | throwout: EventEmitter = new EventEmitter();
35 | throwoutend: EventEmitter = new EventEmitter();
36 | throwoutleft: EventEmitter = new EventEmitter();
37 | throwoutright: EventEmitter = new EventEmitter();
38 | throwoutup: EventEmitter = new EventEmitter();
39 | throwoutdown: EventEmitter = new EventEmitter();
40 | throwin: EventEmitter = new EventEmitter();
41 | throwinend: EventEmitter = new EventEmitter();
42 |
43 | dragstart: EventEmitter = new EventEmitter();
44 | dragmove: EventEmitter = new EventEmitter();
45 | dragend: EventEmitter = new EventEmitter();
46 |
47 | cards: SwingCardComponent[];
48 | stack: Stack;
49 |
50 | constructor() {
51 | this.cards = [];
52 | }
53 |
54 | addCard(card: SwingCardComponent, prepend: boolean = false) {
55 | this.cards.push(card);
56 | if (this.stack) {
57 | this.stack.createCard(card.getNativeElement(), prepend);
58 | }
59 | }
60 |
61 | ngAfterContentInit() {
62 | this.stack = Swing.Stack(this.stackConfig || {});
63 | this.cards.forEach((c) => this.stack.createCard(c.getNativeElement(), c.prepend));
64 |
65 | // Hook various events
66 | this.stack.on('throwout', $event => this.throwout.emit($event));
67 | this.stack.on('throwoutend', $event => this.throwoutend.emit($event));
68 | this.stack.on('throwoutleft', $event => this.throwoutleft.emit($event));
69 | this.stack.on('throwoutright', $event => this.throwoutright.emit($event));
70 | this.stack.on('throwin', $event => this.throwin.emit($event));
71 | this.stack.on('throwinend', $event => this.throwinend.emit($event));
72 | this.stack.on('dragstart', $event => this.dragstart.emit($event));
73 | this.stack.on('dragmove', $event => this.dragmove.emit($event));
74 | this.stack.on('dragend', $event => this.dragend.emit($event));
75 | this.stack.on('throwoutup', $event => this.throwoutup.emit($event));
76 | this.stack.on('throwoutdown', $event => this.throwoutdown.emit($event));
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/swing.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { CommonModule } from '@angular/common';
3 |
4 | import { SwingStackComponent } from './swing-stack-component';
5 | import { SwingCardComponent } from './swing-card-component';
6 |
7 | @NgModule({
8 | imports: [CommonModule],
9 | declarations: [SwingStackComponent, SwingCardComponent],
10 | exports: [SwingCardComponent, SwingStackComponent]
11 | })
12 | export class SwingModule { }
13 |
--------------------------------------------------------------------------------
/src/swing.ts:
--------------------------------------------------------------------------------
1 | // here the ambient definitions for the swing
2 | // module are specified. Normally they should be at DefinitelyTyped
3 | // or better with the repository
4 | declare var require: any;
5 | const Swing = require('swing');
6 |
7 | export interface ThrowEvent {
8 | /**
9 | * The element being dragged.
10 | */
11 | target: HTMLElement;
12 |
13 | /**
14 | * The direction in which the element is being dragged: Card.DIRECTION_LEFT
15 | * or Card.DIRECTION_RIGHT
16 | */
17 | throwDirection: Direction;
18 | }
19 |
20 | export interface DragEvent {
21 | /**
22 | * The element being dragged.
23 | */
24 | target: HTMLElement;
25 |
26 | /**
27 | * Only available when the event is dragmove
28 | */
29 | throwOutConfidence?: number;
30 | /**
31 | * Only available when the event is dragmove
32 | */
33 | throwDirection?: Direction;
34 | /**
35 | * Only available when the event is dragmove
36 | */
37 | offset?: number;
38 |
39 | }
40 |
41 | export type ThrowEventName = 'throwin' | 'throwinend' |
42 | 'throwout' | 'throwoutend' | 'throwoutleft' | 'throwoutup' | 'throwoutdown' | 'throwoutright';
43 |
44 | export type DragEventName = 'dragstart' | 'dragmove' | 'dragend';
45 |
46 | export interface Card {
47 | /**
48 | * Unbinds all Hammer.Manager events.
49 | * Removes the listeners from the physics simulation.
50 | *
51 | * @return {undefined}
52 | */
53 | destroy(): void;
54 |
55 | /**
56 | * Throws a card into the stack from an arbitrary position.
57 | *
58 | * @param {Number} fromX
59 | * @param {Number} fromY
60 | * @return {undefined}
61 | */
62 | throwIn(x: number, y: number): void;
63 |
64 | /**
65 | * Throws a card out of the stack in the direction away from the original offset.
66 | *
67 | * @param {Number} fromX
68 | * @param {Number} fromY
69 | * @return {undefined}
70 | */
71 | throwOut(x: number, y: number): void;
72 |
73 | on(eventName: ThrowEventName, callabck: (event: ThrowEvent) => void): void;
74 | on(eventName: DragEventName, callabck: (event: DragEvent) => void): void;
75 | }
76 |
77 | export interface Stack {
78 |
79 | /**
80 | * Creates an instance of Card and associates it with an element.
81 | *
82 | * @param {HTMLElement} element
83 | * @param {boolean} prepend
84 | * @return {Card}
85 | */
86 | createCard(elment: HTMLElement, prepend?: boolean): void;
87 |
88 | /**
89 | * Returns an instance of Card associated with an element.
90 | *
91 | * @param {HTMLElement} element
92 | * @return {Card|null}
93 | */
94 | getCard(element: HTMLElement): Card;
95 |
96 | /**
97 | *
98 | *@param {Card} card
99 | */
100 | destroyCard(card: Card): void;
101 |
102 | on(eventName: ThrowEventName, callabck: (event: ThrowEvent) => void): void;
103 | on(eventName: DragEventName, callabck: (event: DragEvent) => void): void;
104 | }
105 |
106 | export interface StackConfig {
107 |
108 | minThrowOutDistance?: number;
109 | maxThrowOutDistance?: number;
110 | maxRotation?: number;
111 | allowedDirections?: Array;
112 |
113 | /**
114 | * Determines if element is being thrown out of the stack.
115 | *
116 | * Element is considered to be thrown out when throwOutConfidence is equal to 1.
117 | *
118 | * @param {Number} offset Distance from the dragStart.
119 | * @param {HTMLElement} element Element.
120 | * @param {Number} throwOutConfidence config.throwOutConfidence
121 | * @return {Boolean}
122 | */
123 | isThrowOut?: (offset: number, element: HTMLElement, throwOutConfidence: number) => boolean;
124 |
125 | /**
126 | * Returns a value between 0 and 1 indicating the completeness of the throw out condition.
127 | *
128 | * Ration of the absolute distance from the original card position and element width.
129 | *
130 | * @param {Number} offsetX Distance from the dragStart.
131 | * @param {Number} offsetY Distance from the dragStart.
132 | * @param {HTMLElement} element Element.
133 | * @return {Number}
134 | */
135 | throwOutConfidence?: (offsetX: number, offsetY: number, element: HTMLElement) => number;
136 |
137 | /**
138 | * Calculates a distances at which the card is thrown out of the stack.
139 | *
140 | * @param {Number} min
141 | * @param {Number} max
142 | * @return {Number}
143 | */
144 | throwOutDistance?: (min: number, max: number) => number;
145 |
146 | /**
147 | * Calculates rotation based on the element x and y offset, element width and
148 | * maxRotation variables.
149 | *
150 | * @param {Number} x Horizontal offset from the startDrag.
151 | * @param {Number} y Vertical offset from the startDrag.
152 | * @param {HTMLElement} element Element.
153 | * @param {Number} maxRotation
154 | * @return {Number} Rotation angle expressed in degrees.
155 | */
156 | rotation?: (x: number, y: number, element: HTMLElement, maxRotation: number) => number;
157 |
158 | /**
159 | * Uses CSS transform to translate element position and rotation.
160 | *
161 | * Invoked in the event of `dragmove` and every time the physics solver is triggered.
162 | *
163 | * @param {HTMLElement} element
164 | * @param {Number} x Horizontal offset from the startDrag.
165 | * @param {Number} y Vertical offset from the startDrag.
166 | * @param {Number} r
167 | * @return {undefined}
168 | */
169 | transform?: (element: HTMLElement, x: number, y: number, r: number) => void;
170 | }
171 |
172 | export enum Direction {
173 | DOWN = Swing.Direction.DOWN,
174 | INVALID = Swing.Direction.INVALID,
175 | LEFT = Swing.Direction.LEFT,
176 | RIGHT = Swing.Direction.RIGHT,
177 | UP = Swing.Direction.UP
178 | }
179 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "es2015",
5 | "declaration": true,
6 | "module": "commonjs",
7 | "emitDecoratorMetadata": true,
8 | "experimentalDecorators": true,
9 | "sourceMap": true,
10 | "outDir": "dist",
11 | "removeComments": false
12 | },
13 | "angularCompilerOptions": {
14 | "genDir": "aot"
15 | },
16 | "include": [
17 | "src/**/*",
18 | "typings/**/*"
19 | ],
20 | "exclude": [
21 | "node_modules",
22 | "typings/main.d.ts",
23 | "typings/main"
24 | ],
25 | "compileOnSave": false,
26 | "buildOnSave": false,
27 | "atom": {
28 | "rewriteTsconfig": false
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "class-name": true,
4 | "curly": false,
5 | "eofline": true,
6 | "indent": [
7 | true,
8 | "spaces"
9 | ],
10 | "max-line-length": [
11 | true,
12 | 100
13 | ],
14 | "member-ordering": [
15 | true,
16 | "public-before-private",
17 | "static-before-instance",
18 | "variables-before-functions"
19 | ],
20 | "no-arg": true,
21 | "no-construct": true,
22 | "no-duplicate-key": true,
23 | "no-duplicate-variable": true,
24 | "no-empty": false,
25 | "no-eval": true,
26 | "trailing-comma": true,
27 | "no-trailing-whitespace": true,
28 | "no-unused-expression": true,
29 | "no-unused-variable": false,
30 | "no-unreachable": true,
31 | "no-use-before-declare": true,
32 | "one-line": [
33 | true,
34 | "check-open-brace",
35 | "check-catch",
36 | "check-else",
37 | "check-whitespace"
38 | ],
39 | "quotemark": [
40 | true,
41 | "single"
42 | ],
43 | "semicolon": true,
44 | "triple-equals": [
45 | true,
46 | "allow-null-check"
47 | ],
48 | "variable-name": false,
49 | "whitespace": [
50 | true,
51 | "check-branch",
52 | "check-decl",
53 | "check-operator",
54 | "check-separator",
55 | "check-type"
56 | ]
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/typings.json:
--------------------------------------------------------------------------------
1 | {
2 | "globalDependencies": {
3 | "core-js": "registry:dt/core-js#0.0.0+20160725163759"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------