This page demonstrates interactive FRP widgets powered by Widget and Note.
#require "note";;
#require "js_top_worker-widget";;A simple widget that renders static HTML:
let open Widget.View in
Widget.display ~id:"hello" ~handlers:[]
(Element { tag = "div"; attrs = [];
children = [Text "Hello from an OCaml widget!"] })Counter (FRP with Note)
A counter driven by Note signals. Pressing the buttons sends events back to the worker, which updates the signal:
let inc_e, send_inc = Note.E.create ()
let dec_e, send_dec = Note.E.create ()
let count =
let delta = Note.E.select [
Note.E.map (fun () n -> n + 1) inc_e;
Note.E.map (fun () n -> n - 1) dec_e;
] in
Note.S.accum 0 delta
let counter_view n =
let open Widget.View in
Element { tag = "div"; attrs = [Class "counter"]; children = [
Element { tag = "button";
attrs = [Handler ("click", "dec")];
children = [Text "-"] };
Element { tag = "span";
attrs = [Style ("margin", "0 1em")];
children = [Text (string_of_int n)] };
Element { tag = "button";
attrs = [Handler ("click", "inc")];
children = [Text "+"] };
] }
let () =
Widget.display ~id:"counter"
~handlers:[
"inc", (fun _ -> send_inc ());
"dec", (fun _ -> send_dec ());
]
(counter_view 0)
let _logr = Note.S.log
(Note.S.map counter_view count)
(Widget.update ~id:"counter")
let () = Note.Logr.hold _logrSlider
An input slider that drives a signal. Moving the slider sends the value back to the worker:
let x_e, send_x = Note.E.create ()
let x = Note.S.hold 50 x_e
let slider_view v =
let open Widget.View in
Element { tag = "div"; attrs = []; children = [
Element { tag = "label"; attrs = [];
children = [Text (Printf.sprintf "X: %d" v)] };
Element { tag = "input"; attrs = [
Property ("type", "range");
Property ("min", "0");
Property ("max", "100");
Property ("value", string_of_int v);
Handler ("input", "x");
]; children = [] };
] }
let () =
Widget.display ~id:"slider"
~handlers:["x", (fun v ->
send_x (int_of_string (Option.get v)))]
(slider_view 50)
let _logr2 = Note.S.log
(Note.S.map slider_view x)
(Widget.update ~id:"slider")
let () = Note.Logr.hold _logr2This widget derives from the slider signal x defined above. Moving the slider updates this widget too:
let doubled_view v =
let open Widget.View in
Element { tag = "div"; attrs = []; children = [
Text (Printf.sprintf "2x = %d" (v * 2))
] }
let () =
Widget.display ~id:"doubled"
~handlers:[]
(doubled_view (Note.S.value x))
let _logr3 = Note.S.log
(Note.S.map doubled_view x)
(Widget.update ~id:"doubled")
let () = Note.Logr.hold _logr3Text Entry
A text input with a button. Typing in the textarea fires text_changed, which updates the signal. Clicking "Shout" reads the current text and displays it in uppercase:
let text_e, send_text = Note.E.create ()
let text_s = Note.S.hold "hello world" text_e
let shout_e, send_shout = Note.E.create ()
let shouted = Note.S.hold "" shout_e
let text_entry_view txt =
let open Widget.View in
Element { tag = "div"; attrs = []; children = [
Element { tag = "textarea"; attrs = [
Property ("rows", "2");
Handler ("input", "text_changed");
]; children = [Text txt] };
Element { tag = "button"; attrs = [
Handler ("click", "shout");
]; children = [Text "Shout"] };
] }
let result_view s =
let open Widget.View in
Element { tag = "div"; attrs = [
Style ("font-family", "monospace");
Style ("padding", "0.5em");
]; children = [Text s] }
let () =
Widget.display ~id:"text-entry"
~handlers:[
"text_changed", (fun v -> send_text (Option.value ~default:"" v));
"shout", (fun _ ->
let current = Note.S.value text_s in
send_shout (String.uppercase_ascii current));
]
(text_entry_view "hello world")
let () =
Widget.display ~id:"text-result"
~handlers:[]
(result_view "")
let _logr_text = Note.S.log text_s (fun _ -> ())
let () = Note.Logr.hold _logr_text
let _logr4 = Note.S.log
(Note.S.map result_view shouted)
(Widget.update ~id:"text-result")
let () = Note.Logr.hold _logr4