-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathTodoListComponent.ml
142 lines (132 loc) · 3.95 KB
/
TodoListComponent.ml
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
140
141
142
open OCamlMVC
open Printf
let (<.>) f g x = f (g x)
(**********************************************************************)
type state = TodoList.t
type action =
| Add
| Set of int * TodoList.status
| Clear
| UpdatePending of string
let string_of_action = function
| Add -> "Add"
| Set (i, TodoList.TODO) -> sprintf "Set (%d, TODO)" i
| Set (i, TodoList.DONE) -> sprintf "Set (%d, DONE)" i
| Clear -> "Clear"
| UpdatePending s -> sprintf "UpdatePending %S" s
(**********************************************************************)
let render_item idx {TodoList.label=l;status} =
let id = sprintf "checkbox%d" idx in
let open Html in
let open TodoList in
let checkbox ~id ~onchange ~state =
let attrs =
[ A.type_ "checkbox"; A.id id; E.onchange (fun ~value -> onchange) ]
in
let attrs =
if state then A.checked true::attrs else attrs
in
input ~attrs Html.empty
in
match status with
| DONE ->
checkbox ~id ~onchange:(Set (idx, TODO)) ~state:true
^^
label ~attrs:[A.for_control id] (span ~attrs:[A.class_ "done"] (text l))
| TODO ->
checkbox ~id ~onchange:(Set (idx, DONE)) ~state:false
^^
label ~attrs:[A.for_control id] (text l)
let render_items = let open Html in function
| [] ->
em (text "Items will appear here")
| items ->
ul ~attrs:[A.class_ "no-bullet"] begin
of_list (List.mapi (fun i -> li <.> render_item i) items)
end
let render {TodoList.items;pending_item} =
let open Html in
let div ~classes html =
div ~attrs:[A.class_ (String.concat " " classes)] html
in
let text_input ~classes ~onenter ~oninput ~placeholder value =
input
~attrs:[ A.class_ (String.concat " " classes)
; A.type_ "text"
; A.value value
; A.placeholder placeholder
; E.oninput oninput
; E.onkeypress (fun key_code char_code ->
if key_code = Keycode.return
then Some onenter else None)
]
empty
and button ~classes ~enabled ~onclick label =
let classes =
["button"]
@ (if enabled then [] else ["disabled"])
@ classes
in
button ~attrs:[ A.class_ (String.concat " " classes)
; E.onclick onclick
]
(text label)
in
div ~classes:["panel";"radius"] begin
div ~classes:["row";"collapse"] begin
div ~classes:["small-8";"columns"] begin
text_input
~classes:["radius"]
~onenter:Add
~oninput:(fun ~value -> UpdatePending value)
~placeholder:"Enter new item here"
pending_item
end
^^
div ~classes:["small-2";"columns"] begin
button
~classes:["postfix"]
~enabled:(pending_item <> "")
~onclick:Add
"Add"
end
^^
div ~classes:["small-2";"columns"] begin
button
~classes:["radius";"postfix";"alert"]
~enabled:(List.exists (fun item -> item.TodoList.status = TodoList.DONE) items)
~onclick:Clear
"Clear"
end
^^
render_items items
^^
hr ()
^^
div ~classes:["row"] begin
div ~classes:["small-12";"columns"] begin
let num_items = List.length items in
let num_done_items =
List.length (List.filter (fun i -> i.TodoList.status = TodoList.DONE) items)
in
text (sprintf "%d item%s with %d completed"
num_items
(if num_items = 1 then "" else "s")
num_done_items)
end
end
end
end
let update = let open TodoList in function
| Add -> add_pending_item
| Set (idx, status) -> set idx status
| Clear -> clear
| UpdatePending text -> update_pending text
let initial =
TodoList.empty
module ActionFilter = struct
type t = action
let relevant = function
| UpdatePending _ -> false
| _ -> true
end