-
Notifications
You must be signed in to change notification settings - Fork 0
/
VCR.ml
120 lines (112 loc) · 3.34 KB
/
VCR.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
module type Component = sig
include OCamlMVC.Component
val string_of_action : action -> string
end
module type Filter = sig
type t
val relevant : t -> bool
end
module Of
(Inner : Component)
(Filter : Filter with type t = Inner.action)
: OCamlMVC.Component
=
struct
open OCamlMVC
type state =
{ history : (Inner.state * Inner.action) list
; now : Inner.state
; future : (Inner.action * Inner.state) list
}
type action =
| Inner of Inner.action
| Undo
| Redo
let render_truncated_list ~f ~limit list =
let open Html in
let render_item x =
li (span ~attrs:[A.class_ "small"] (text x))
in
let rec loop i = function
| [] -> empty
| x::xs when i = limit -> render_item "..."
| x::xs -> render_item (f x) ^^ loop (i+1) xs
in
loop 0 list
let render {history;now;future} =
let open Html in
let have_history = history <> [] in
let have_future = future <> [] in
let div ~classes children =
div ~attrs:[A.class_ (String.concat " " classes)] children
in
let ul ~classes children =
ul ~attrs:[A.class_ (String.concat " " classes)] children
in
let button ~enabled ~onclick label =
button ~attrs:[A.enabled enabled; E.onclick onclick] (text label)
in
div ~classes:["row"] begin
div ~classes:["columns";"large-7"] begin
map (fun action -> Inner action) (Inner.render now)
end
^^
div ~classes:["columns";"large-5"] begin
div ~classes:["row"] begin
div ~classes:["small-centered";"small-12";"columns"] begin
ul ~classes:["button-group";"radius"] begin
li (button ~enabled:have_history ~onclick:Undo "« Undo")
^^
li (button ~enabled:have_future ~onclick:Redo "Redo »")
end
end
end
^^
div ~classes:["row"] begin
div ~classes:["small-6";"columns"] begin
h6 (text "History")
^^
ul ~classes:["no-bullet"] begin
history
|> render_truncated_list
~f:(fun (_,act) -> Inner.string_of_action act)
~limit:10
end
end
^^
div ~classes:["small-6";"columns"] begin
h6 (text "Future")
^^
ul ~classes:["no-bullet"] begin
future
|> render_truncated_list
~f:(fun (act,_) -> Inner.string_of_action act)
~limit:10
end
end
end
end
end
let update = function
| Inner action when Filter.relevant action ->
(fun {history;now} ->
{ history = (now,action)::history
; now = Inner.update action now
; future = []
})
| Inner action ->
(fun ({now} as t) ->
{t with now = Inner.update action now})
| Undo ->
(function
| {history=[]} as state -> state
| {history=(prev,action)::history; now; future} ->
{history; now=prev; future=(action,now)::future})
| Redo ->
(function
| {future=[]} as state -> state
| {future=(action,next)::future; now; history} ->
{history=(now,action)::history; now=next; future})
let initial =
{ history = []; now = Inner.initial; future = [] }
end