/
main.ml
128 lines (107 loc) · 3.14 KB
/
main.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
module H = Fmlib_browser.Html
module A = Fmlib_browser.Attribute
(* [@@@warning "-a-l-l"] *)
let ( >>= ) = Option.bind
(*
* MODEL
*)
type celsius = Celsius of float
type fahrenheit = Fahrenheit of float
type model =
{ celsius_inp : string option
; celsius_conv : celsius option
; fahrenheit_inp : string option
; fahrenheit_conv : fahrenheit option
}
let fahrenheit_of_celsius (Celsius c) =
let value = (c *. (9.0 /. 5.0)) +. 32.0 in
Fahrenheit value
;;
let celsius_of_fahrenheit (Fahrenheit f) =
let value = (f -. 32.0) *. (5.0 /. 9.0) in
Celsius value
;;
let celsius_of_string_opt str =
str |> float_of_string_opt |> Option.map (fun x -> Celsius x)
;;
let fahrenheit_of_string_opt str =
str |> float_of_string_opt |> Option.map (fun x -> Fahrenheit x)
;;
let string_of_float' = Printf.sprintf "%.2f"
let string_of_fahrenheit (Fahrenheit v) = string_of_float' v
let string_of_celsius (Celsius v) = string_of_float' v
let init : model =
{ celsius_inp = None
; celsius_conv = None
; fahrenheit_inp = None
; fahrenheit_conv = None
}
;;
(*
* UPDATE
*)
type msg = CelsiusChanged of string | FahrenheitChanged of string
let non_empty = function
| "" -> None
| x -> Some x
;;
let update (_model : model) msg =
match msg with
| CelsiusChanged str ->
let celsius_inp = non_empty str in
let celsius_conv = celsius_inp >>= celsius_of_string_opt in
let fahrenheit_conv = celsius_conv |> Option.map fahrenheit_of_celsius in
let fahrenheit_inp = fahrenheit_conv |> Option.map string_of_fahrenheit in
{ celsius_inp; celsius_conv; fahrenheit_conv; fahrenheit_inp }
| FahrenheitChanged str ->
let fahrenheit_inp = non_empty str in
let fahrenheit_conv = fahrenheit_inp >>= fahrenheit_of_string_opt in
let celsius_conv =
str |> fahrenheit_of_string_opt |> Option.map celsius_of_fahrenheit
in
let celsius_inp = celsius_conv |> Option.map string_of_celsius in
{ fahrenheit_inp; fahrenheit_conv; celsius_conv; celsius_inp }
;;
(*
* VIEW
*)
let form_ctrl = A.class_ "form-ctrl"
let bg inp conv =
let bad_input = inp <> None && conv = None in
if bad_input then
"red"
else
""
;;
let view model =
H.div []
[ H.h1 [] [ H.text "Temp converter" ]
; H.div
[ A.id "convert-area" ]
[ H.div [ form_ctrl ]
[ H.input
[ A.on_input (fun s -> CelsiusChanged s)
; A.attribute "autofocus" ""
; A.value (model.celsius_inp |> Option.value ~default:"")
; A.background_color (bg model.celsius_inp model.celsius_conv)
]
[]
; H.label [] [ H.text "Celsius" ]
]
; H.div [ form_ctrl ]
[ H.input
[ A.on_input (fun s -> FahrenheitChanged s)
; A.value (model.fahrenheit_inp |> Option.value ~default:"")
; A.background_color
(bg model.fahrenheit_inp model.fahrenheit_conv)
]
[]
; H.label [] [ H.text "Fahrenheit" ]
]
]
]
;;
(*
* BOOTSTRAP
*)
let () = Fmlib_browser.sandbox init view update