/
List.js
105 lines (89 loc) · 2.89 KB
/
List.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import xs from 'xstream';
import {button, div} from '@cycle/dom';
import isolate from '@cycle/isolate'
import Item from './Item';
function intent(domSource, itemRemove$) {
return xs.merge(
domSource.select('.add-one-btn').events('click')
.mapTo({type: 'ADD_ITEM', payload: 1}),
domSource.select('.add-many-btn').events('click')
.mapTo({type: 'ADD_ITEM', payload: 1000}),
itemRemove$.map(id => ({type: 'REMOVE_ITEM', payload: id}))
);
}
function model(action$, itemFn) {
function createRandomItemProps() {
let hexColor = Math.floor(Math.random() * 16777215).toString(16);
while (hexColor.length < 6) {
hexColor = '0' + hexColor;
}
hexColor = '#' + hexColor;
const randomWidth = Math.floor(Math.random() * 800 + 200);
return {color: hexColor, width: randomWidth};
}
let mutableLastId = 0;
function createNewItem(props) {
const id = mutableLastId++;
const sinks = itemFn(props, id);
return {id, DOM: sinks.DOM.remember(), Remove: sinks.Remove};
}
const addItemReducer$ = action$
.filter(a => a.type === 'ADD_ITEM')
.map(action => {
const amount = action.payload;
let newItems = [];
for (let i = 0; i < amount; i++) {
newItems.push(createNewItem(createRandomItemProps()));
}
return function addItemReducer(listItems) {
return listItems.concat(newItems);
};
});
const removeItemReducer$ = action$
.filter(a => a.type === 'REMOVE_ITEM')
.map(action => function removeItemReducer(listItems) {
return listItems.filter(item => item.id !== action.payload);
});
const initialState = [createNewItem({color: 'red', width: 300})]
return xs.merge(addItemReducer$, removeItemReducer$)
.fold((listItems, reducer) => reducer(listItems), initialState);
}
function view(items$) {
const addButtons = div('.addButtons', [
button('.add-one-btn', 'Add New Item'),
button('.add-many-btn', 'Add Many Items')
]);
return items$.map(items => {
const itemVNodeStreamsByKey = items.map(item =>
item.DOM.map(vnode => {
vnode.key = item.id; return vnode;
})
);
return xs.combine(...itemVNodeStreamsByKey)
.map(vnodes => div('.list', [addButtons].concat(vnodes)));
}).flatten();
}
function makeItemWrapper(DOM) {
return function itemWrapper(props, id) {
const item = isolate(Item)({DOM, Props: xs.of(props)});
return {
DOM: item.DOM,
Remove: item.Remove.mapTo(id)
}
}
}
function List(sources) {
const proxyItemRemove$ = xs.create();
const action$ = intent(sources.DOM, proxyItemRemove$);
const itemWrapper = makeItemWrapper(sources.DOM);
const items$ = model(action$, itemWrapper);
const itemRemove$ = items$
.map(items => xs.merge(...items.map(item => item.Remove)))
.flatten();
proxyItemRemove$.imitate(itemRemove$);
const vtree$ = view(items$);
return {
DOM: vtree$
};
}
export default List;