Skip to content

Commit cc26986

Browse files
committed
wip(card-list): @ngrx/store helpers to make building actions/reducers much easier
(~50 LOC of boilerplate saved in the Kanban example, as predicted in #69)
1 parent f0da1d1 commit cc26986

File tree

7 files changed

+242
-182
lines changed

7 files changed

+242
-182
lines changed

packages/angular-skyhook-card-list/public-api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ export { SkyhookCardListModule } from "./src/module";
1010
export { SortableSpec } from "./src/SortableSpec";
1111
export { SimpleSortable } from "./src/SimpleSortable";
1212
export { SharedSortableService } from "./src/SharedSortableService";
13+
export * from "./src/ngrx-helpers";

packages/angular-skyhook-card-list/src/SharedSortableService.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ export interface ListsById<C> {
1010
[k: string]: C[];
1111
}
1212

13+
interface OnlySupported<C extends Data> {
14+
trackBy: (data: C) => any;
15+
canDrag?: (data: C, listId: any) => boolean;
16+
canDrop?: (item: DraggedItem<C>) => boolean;
17+
}
18+
1319
export interface GroupOptions<C extends Data> {
1420
trackBy: (data: C) => any;
1521
copy?: (item: DraggedItem<C>) => boolean;
@@ -152,7 +158,7 @@ export class SharedSortableService<C extends Data = any> implements OnDestroy {
152158

153159
private makeSpec(type: string | symbol, options: GroupOptions<C>): SortableSpec<C> {
154160
return {
155-
...options,
161+
...options as OnlySupported<C>,
156162
getList: (listId: any) => {
157163
return this.listFor(type, listId);
158164
},

packages/angular-skyhook-card-list/src/SortableSpec.ts

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,15 @@ import { DraggedItem } from "./dragged-item";
22
import { Data } from "./data";
33
import { Observable } from 'rxjs';
44

5-
export interface SortableSpec<T extends Data = Data> {
6-
removeOnSpill?: boolean,
7-
revertOnSpill?: boolean,
8-
trackBy: (data: T) => any;
9-
getList: (listId: any) => Observable<Iterable<T> | undefined>;
10-
canDrag?: (data: T, listId: any) => boolean;
11-
canDrop?: (item: DraggedItem<T>) => boolean;
12-
copy?: (item: DraggedItem<T>) => T | boolean;
13-
clone?: (data: T) => T;
14-
beginDrag?: (item: DraggedItem<T>) => void;
15-
hover?: (item: DraggedItem<T>) => void;
16-
drop?: (item: DraggedItem<T>) => void;
17-
endDrag?: (item: DraggedItem<T>) => void;
5+
export interface SortableSpec<D extends Data = Data, Type = string|symbol> {
6+
trackBy: (data: D) => any;
7+
getList: (listId: any) => Observable<Iterable<D> | undefined>;
8+
canDrag?: (data: D, listId: any) => boolean;
9+
canDrop?: (item: DraggedItem<D, Type>) => boolean;
10+
beginDrag?: (item: DraggedItem<D, Type>) => void;
11+
hover?: (item: DraggedItem<D, Type>) => void;
12+
drop?: (item: DraggedItem<D, Type>) => void;
13+
endDrag?: (item: DraggedItem<D, Type>) => void;
14+
// copy?: (item: DraggedItem<T>) => T | boolean;
15+
// clone?: (data: T) => T;
1816
}

packages/angular-skyhook-card-list/src/card-renderer.directive.ts

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -66,32 +66,32 @@ export class CardRendererDirective implements OnInit, OnDestroy {
6666
listId: this.listId
6767
}
6868
};
69-
if (this.spec && this.spec.copy && this.spec.copy(item)) {
70-
if (!this.spec.clone) {
71-
throw new Error("must provide clone function");
72-
}
73-
let clone = this.spec.clone(item.data);
74-
if (clone !== item.data || this.sameIds(item.data, clone)) {
75-
throw new Error("clone must return a new object with a different id / trackBy result");
76-
}
77-
item.data = clone;
78-
item.hover.index ++;
79-
item.isCopy = true;
80-
}
69+
// if (this.spec && this.spec.copy && this.spec.copy(item)) {
70+
// if (!this.spec.clone) {
71+
// throw new Error("must provide clone function");
72+
// }
73+
// let clone = this.spec.clone(item.data);
74+
// if (clone !== item.data || this.sameIds(item.data, clone)) {
75+
// throw new Error("clone must return a new object with a different id / trackBy result");
76+
// }
77+
// item.data = clone;
78+
// item.hover.index ++;
79+
// item.isCopy = true;
80+
// }
8181
this.spec && this.spec.beginDrag && this.spec.beginDrag(item);
82-
if (item.isCopy) {
83-
// Chrome bug means an immediate dragend would fire
84-
// if you did this synchronously
85-
let canDrop = true;
86-
if (this.spec && this.spec.canDrop) {
87-
canDrop = this.spec.canDrop(item);
88-
}
89-
if (canDrop) {
90-
setTimeout(() => {
91-
this.spec && this.spec.hover && this.spec.hover(item);
92-
}, 0);
93-
}
94-
}
82+
// if (item.isCopy) {
83+
// // Chrome bug means an immediate dragend would fire
84+
// // if you did this synchronously
85+
// let canDrop = true;
86+
// if (this.spec && this.spec.canDrop) {
87+
// canDrop = this.spec.canDrop(item);
88+
// }
89+
// if (canDrop) {
90+
// setTimeout(() => {
91+
// this.spec && this.spec.hover && this.spec.hover(item);
92+
// }, 0);
93+
// }
94+
// }
9595
return item;
9696
},
9797
endDrag: monitor => {

packages/angular-skyhook-card-list/src/dragged-item.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { Data } from "./data";
22
import { Size } from "./size";
33

4-
export interface DraggedItem<D = Data> {
4+
export interface DraggedItem<D = Data, DndType = string | symbol> {
55
data: D;
66
size: Size;
77
index: number;
8-
type: string | symbol;
8+
type: DndType;
99
listId: any;
1010
isInternal?: boolean;
1111
isCopy: boolean;
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { DraggedItem } from './dragged-item';
2+
import { SortableSpec } from './SortableSpec';
3+
import { Observable } from 'rxjs';
4+
5+
export enum SortableEvents {
6+
BeginDrag = "BeginDrag",
7+
Hover = "Hover",
8+
Drop = "Drop",
9+
EndDrag = "EndDrag",
10+
}
11+
12+
export class BeginDrag<ActionType, T> {
13+
readonly event = SortableEvents.BeginDrag;
14+
constructor(public readonly type: ActionType, public readonly item: DraggedItem<T>) {}
15+
}
16+
17+
export class Hover<ActionType, T> {
18+
readonly event = SortableEvents.Hover;
19+
constructor(public readonly type: ActionType, public readonly item: DraggedItem<T>) {}
20+
}
21+
22+
export class Drop<ActionType, T> {
23+
readonly event = SortableEvents.Drop;
24+
constructor(public readonly type: ActionType, public readonly item: DraggedItem<T>) {}
25+
}
26+
27+
export class EndDrag<ActionType, T> {
28+
readonly event = SortableEvents.EndDrag;
29+
constructor(public readonly type: ActionType, public readonly item: DraggedItem<T>) {}
30+
}
31+
32+
export type SortableAction<ActionType, D> = BeginDrag<ActionType, D> | Hover<ActionType, D> | Drop<ActionType, D> | EndDrag<ActionType, D>;
33+
34+
/** Intended to be your NgRx Store object */
35+
export interface Dispatcher {
36+
dispatch: (action: SortableAction<any, any>) => void;
37+
}
38+
39+
export interface ConfigureSpec<D> {
40+
trackBy: (data: D) => any;
41+
getList: (listId: any) => Observable<Iterable<D>>;
42+
canDrag?: (data: D, listId: any) => boolean;
43+
canDrop?: (item: DraggedItem<D>) => boolean;
44+
// copy?: (item: DraggedItem<T>) => boolean | T;
45+
// clone?: (data: T) => T;
46+
}
47+
48+
export class NgRxSortable<D> implements SortableSpec<D> {
49+
public trackBy!: (data: D) => any;
50+
public getList!: (listId: any) => Observable<Iterable<D>>;
51+
public canDrag?: (data: D, listId: any) => boolean;
52+
public canDrop?: (item: DraggedItem<D>) => boolean;
53+
54+
/**
55+
* @param store An @ngrx store instance.
56+
* @param actionType The type in your own @ngrx/store `ActionTypes` enum you want the sortable actions to use.
57+
* @param configure You must provide `trackBy` and `getList` functions here. Hopefully your `getList` will select from the store you passed!
58+
* */
59+
constructor(private store: Dispatcher, public actionType: string, configure: ConfigureSpec<D>) {
60+
this.trackBy = configure.trackBy;
61+
this.getList = configure.getList;
62+
this.canDrag = configure.canDrag;
63+
this.canDrop = configure.canDrop;
64+
}
65+
66+
// We now implement the SortableSpec interface by dispatching actions
67+
68+
beginDrag = (item: DraggedItem<D>) => {
69+
this.store.dispatch(new BeginDrag(this.actionType, item));
70+
}
71+
hover = (item: DraggedItem<D>) => {
72+
this.store.dispatch(new Hover(this.actionType, item));
73+
}
74+
drop = (item: DraggedItem<D>) => {
75+
this.store.dispatch(new Drop(this.actionType, item));
76+
}
77+
endDrag = (item: DraggedItem<D>) => {
78+
this.store.dispatch(new EndDrag(this.actionType, item));
79+
}
80+
}

0 commit comments

Comments
 (0)