-
Notifications
You must be signed in to change notification settings - Fork 0
/
List.js
113 lines (99 loc) · 2.96 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
106
107
108
109
110
111
112
113
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() * 300 + 100)
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