-
Notifications
You must be signed in to change notification settings - Fork 5
/
Basic.purs
139 lines (130 loc) · 4.21 KB
/
Basic.purs
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
module Example.Basic where
import Prelude
import Data.Array ((!!), drop, mapWithIndex, take)
import Data.Foldable (traverse_)
import Data.Int as Int
import Data.Maybe (fromMaybe, maybe, Maybe(..))
import Effect (Effect)
import Effect.Class.Console as Console
import Effect.Exception (throw)
import React.Basic.DOM (render)
import React.Basic.DOM as R
import React.Basic.DOM.Events (targetChecked)
import React.Basic.Events as Events
import React.Basic.Hooks (Component, component, fragment, mkReducer, useReducer, (/\))
import React.Basic.Hooks as React
import React.Basic.ReactDND (dndProvider, mergeTargets, useDrag, useDrop)
import React.Basic.ReactDND.Backends.HTML5Backend (html5Backend)
import Web.DOM.NonElementParentNode (getElementById)
import Web.HTML (window)
import Web.HTML.HTMLDocument (toNonElementParentNode)
import Web.HTML.Window (document)
data Action
= Move { from :: Int, to :: Int }
| SetDone String Boolean
type Todo = { id :: String, text :: String, done :: Boolean }
mkTodoExample :: Component Unit
mkTodoExample = do
todo <- mkTodo
reducer <- mkReducer update
React.component "TodoExample" \_ -> React.do
state /\ dispatch <- useReducer initialState reducer
pure
$ dndProvider html5Backend
$ fragment
[ R.h1_ [ R.text "Todos" ]
, R.p_ [ R.text "Drag to reorder the list:" ]
, R.section_
$ state.todos
# mapWithIndex \index t -> todo { index, todo: t, dispatch }
]
where
initialState =
{ todos:
[ { id: "a", text: "PureScript", done: true }
, { id: "b", text: "React Basic", done: true }
, { id: "c", text: "React Basic DND", done: false }
]
}
update state = case _ of
Move { from, to } ->
state
{ todos = moveItem from to state.todos
}
SetDone id done ->
state
{ todos =
state.todos
<#> \t ->
if t.id == id then
t { done = done }
else
t
}
mkTodo
:: Component
{ index :: Int
, todo :: Todo
, dispatch :: Action -> Effect Unit
}
mkTodo = do
let
todoDND = "todo-dnd"
component "Todo" \{ index, todo, dispatch } -> React.do
{ isDragging, connectDrag } <- useDrag { type: todoDND, id: show index }
{ id: maybeDragItem, isOver, connectDrop } <-
useDrop
{ accept: todoDND
, onDrop: Int.fromString >>> traverse_ \id -> dispatch $ Move { from: id, to: index }
}
pure
$ R.label
{ ref: mergeTargets connectDrag connectDrop
, style:
R.css
{ display: "block"
, padding: "0.3rem 0.8rem"
, alignItems: "center"
, borderTop:
if isOver && maybe false ((_ > show index)) maybeDragItem then
"0.2rem solid #0044e4"
else
"0.2rem solid transparent"
, borderBottom:
if isOver && maybe false ((_ < show index)) maybeDragItem then
"0.2rem solid #0044e4"
else
"0.2rem solid transparent"
, opacity: if isDragging then 0.1 else 1.0
}
, children:
[ R.input
{ type: "checkbox"
, checked: todo.done
, onChange:
Events.handler targetChecked \checked -> do
dispatch $ SetDone todo.id $ fromMaybe false checked
}
, R.text todo.text
]
}
moveItem :: forall a. Int -> Int -> Array a -> Array a
moveItem fromIndex toIndex items =
let
item = items !! fromIndex
items' = take fromIndex items <> drop (fromIndex + 1) items
in
take toIndex items'
<> maybe [] pure item
<> drop toIndex items'
main :: Effect Unit
main = do
maybeContainer <- window
>>= document
>>= toNonElementParentNode
>>> getElementById "container"
case maybeContainer of
Nothing -> throw "Container element not found."
Just container -> do
todoExample <- mkTodoExample
render (todoExample unit) container