aboutsummaryrefslogtreecommitdiff
path: root/js/content.ml
blob: 62d4444a34f5ae114f39057d42ad170d4ac54120 (plain)
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
module OptionInfix = Operators.Binding (Option)

module State = struct
  type t = { word : string; len : int; counter : int; selected : Jstr.t }

  let repr_html : t -> Brr.El.t list =
   fun { word; len; counter; selected } ->
    [
      Brr.El.div
        ~at:Brr.At.[ class' (Jstr.v "card") ]
        [
          Brr.El.div
            ~at:Brr.At.[ class' (Jstr.v "card-header") ]
            [
              Brr.El.header
                ~at:Brr.At.[ class' (Jstr.v "card-header") ]
                [
                  Brr.El.p
                    ~at:Brr.At.[ class' (Jstr.v "card-header-title") ]
                    [ Brr.El.txt' "Response from the server" ];
                ];
            ];
          Brr.El.div
            ~at:Brr.At.[ class' (Jstr.v "card-content") ]
            [
              Brr.El.form
                [
                  Elements.Form.input_field ~label:(Jstr.v "Word received")
                    ~value':(Jstr.v word) ();
                  Elements.Form.input_field ~label:(Jstr.v "Nb of car")
                    ~value':(Jstr.of_int len) ();
                  Elements.Form.input_field ~label:(Jstr.v "Request sent")
                    ~value':(Jstr.of_int counter) ();
                  Elements.Form.input_field ~label:(Jstr.v "Selected method")
                    ~value':selected ();
                ];
            ];
        ];
    ]
end

(** Service transforming the response from the request into the state *)
module WordCount = struct
  type t = Services_impl.Nb_car.response

  let process response state =
    Brr.Console.log
      [
        Jstr.v response.Services_impl.Nb_car.value;
        Int64.to_int response.Services_impl.Nb_car.nbcar;
      ];
    State.
      {
        state with
        counter = state.counter + 1;
        word = response.Services_impl.Nb_car.value;
        len = Int64.to_int response.Services_impl.Nb_car.nbcar;
      }
end

module Capitalize = struct
  type t = Services_impl.Capitalize.response

  let process response state =
    Brr.Console.log [ Jstr.v response.Services_impl.Capitalize.value ];
    State.
      {
        state with
        counter = state.counter + 1;
        word = response.Services_impl.Capitalize.value;
      }
end

(** Show how to react to a user event *)
module SelectOption = struct
  type t = Jstr.t

  let process v state = State.{ state with selected = v }
end

module App = Application.Make (State)

let main () =
  let open OptionInfix in
  let- content_div =
    Brr.Document.find_el_by_id Brr.G.document (Jstr.v "content")
  in

  let radio =
    Elements.Form.radio ~label:(Jstr.v "Method") ~name:(Jstr.v "method")
      ~values:
        [
          {
            id' = None;
            label = Jstr.v "Count the letters";
            value = Jstr.v "count";
            checked = true;
          };
          {
            id' = None;
            label = Jstr.v "Capitalize";
            value = Jstr.v "capitalize";
            checked = false;
          };
        ]
      ()
  in

  (* Catch the change event from the radio button list. 

    Each radio button triggers it’s own event when selected, but the event
    bubbles, so we just listen the parent, and target the event source for
    reading the value. *)
  let radio_value =
    Note_brr.Evr.on_el Brr.Ev.change
      (fun evt ->
        let target_as_element : Brr.El.t = Brr.Ev.target evt |> Obj.magic in

        let raw_value = Brr.El.prop Brr.El.Prop.value target_as_element in
        App.dispatch (module SelectOption) raw_value)
      radio
  in

  let form =
    Brr.El.form
      [
        Elements.Form.input_field ~name:(Jstr.v "text") ~id':(Jstr.v "text")
          ~label:(Jstr.v "Text") ();
        radio;
        Elements.Form.submit ~value':(Jstr.v "Send") ();
      ]
  in

  (* Listen the submit event on the form. This is an example of event of event :

     First we listen for the click event, and then for the request response
     event. *)
  let post_event : Brr.El.t -> App.event Note.event =
   fun form ->
    Note.E.join
    @@ Note_brr.Evr.on_el Brr_io.Form.Ev.submit
         (fun ev ->
           (* Do not send the query, we use it with javascript *)
           Brr.Ev.prevent_default ev;

           (* Extract the data from the form *)
           let data = Brr_io.Form.(Data.of_form (of_el form)) in
           let text_value = Brr_io.Form.Data.find data (Jstr.v "text") in
           let value =
             match text_value with
             | Some (`String s) -> Jstr.to_string s
             | _ -> ""
           in

           let service_name =
             Option.bind
               (Brr_io.Form.Data.find data (Jstr.v "method"))
               (function `String s -> Some (Jstr.to_string s) | _ -> None)
           in

           match service_name with
           | Some "count" ->
               (* Send the request *)
               Js_handler.send (module Services_impl.Nb_car) () { value }
               |> Note_brr.Futr.to_event
               |> Note.E.map (function
                    | Error _ -> App.dispatch (module App.ID) ()
                    | Ok response -> App.dispatch (module WordCount) response)
           | Some "capitalize" ->
               (* Send the request *)
               Js_handler.send (module Services_impl.Capitalize) () { value }
               |> Note_brr.Futr.to_event
               |> Note.E.map (function
                    | Error _ -> App.dispatch (module App.ID) ()
                    | Ok response -> App.dispatch (module Capitalize) response)
           | _ -> Note.E.never)
         form
  in
  let bottom = Brr.El.div [] in
  Brr.El.append_children content_div
    [
      Brr.El.div
        ~at:Brr.At.[ class' (Jstr.v "card") ]
        [
          Brr.El.div
            ~at:Brr.At.[ class' (Jstr.v "card-header") ]
            [
              Brr.El.header
                ~at:Brr.At.[ class' (Jstr.v "card-header") ]
                [
                  Brr.El.p
                    ~at:Brr.At.[ class' (Jstr.v "card-header-title") ]
                    [ Brr.El.txt' "Request to the server" ];
                ];
            ];
          Brr.El.div ~at:Brr.At.[ class' (Jstr.v "card-content") ] [ form ];
        ];
      Brr.El.hr ();
      bottom;
    ];

  let state =
    App.run
      { word = ""; len = 0; counter = 0; selected = Jstr.v "count" }
      (Note.E.select [ post_event form; radio_value ])
  in

  Note_brr.Elr.def_children bottom (Note.S.map State.repr_html state);

  let log state = ignore state in
  Note.Logr.hold (Note.S.log state log)

let () =
  Brr.Console.(debug [ Jstr.v "Js started" ]);
  let open Jv in
  let post = obj [| ("start", repr main) |] in
  set global "client" post