Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ v0.4 - Alpha 0.4 UNRELEASED
- Lwd.set: change binding before invalidation, otherwise the old value could be re-observed (reported by @voodoos)
- brr-lwd: support declarative events and set of css classes
- Nottui: fix treatment of some terminal events that were delayed because of improper buffer flushing, reported by @darrenldl
- brr-lwd: Fix handling of multiple reactive attributes (by @panglesd)
- brr-lwd: Add support for reactive inline styles (by @panglesd)

v0.3 - Alpha 0.3
======
Expand Down
9 changes: 9 additions & 0 deletions examples/reactive-styles-brr/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
ROOT=$(realpath $(PWD)/../..)
NAME=$(subst $(ROOT)/,,$(realpath $(PWD)))

all:
dune build index.html main.js
@echo "open $(ROOT)/_build/default/$(NAME)/index.html"

clean:
dune clean
21 changes: 21 additions & 0 deletions examples/reactive-styles-brr/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
(executables
(names main)
(libraries js_of_ocaml brr lwd brr-lwd)
(modes byte))

(rule
(targets main.js)
(action
(run
%{bin:js_of_ocaml}
--noruntime
%{lib:js_of_ocaml-compiler:runtime.js}
--source-map
%{dep:main.bc}
-o
%{targets}
--pretty)))

(alias
(name default)
(deps main.js index.html))
10 changes: 10 additions & 0 deletions examples/reactive-styles-brr/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!doctype html>
<html>
<head>
<title>Reactive styles</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" src="main.js"></script>
</head>
<body>
</body>
</html>
80 changes: 80 additions & 0 deletions examples/reactive-styles-brr/main.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
open Brr
open Brr_lwd
open Lwd_infix

let float var =
let h =
fun ev ->
let el = ev |> Brr.Ev.target |> Brr.Ev.target_to_jv in
let new_value = Jv.get el "value" |> Jv.to_string |> float_of_string in
Brr.Console.(log [ new_value ]);
Lwd.set var new_value
in
let ev = [ `P (Brr_lwd.Elwd.handler Brr.Ev.input h) ] in
let at =
let v =
let$ v = Lwd.get var in
Brr.At.value (Jstr.of_float v)
in
[ `R v; `P (Brr.At.type' (Jstr.v "number")) ]
in
Brr_lwd.Elwd.input ~at ~ev ()

let ui =
let top = Lwd.var 1.0 in
let set_top = float top in
let left = Lwd.var 1.0 in
let set_left = float left in
let block =
let top =
let$ top = Lwd.get top in
let top = Jstr.append (Jstr.of_float top) (Jstr.v "px") in
(El.Style.top, top)
in
let left =
let$ left = Lwd.get left in
let left = Jstr.append (Jstr.of_float left) (Jstr.v "px") in
(El.Style.left, left)
in
let st =
[
`R top;
`R left;
`P (El.Style.width, Jstr.v "50px");
`P (El.Style.height, Jstr.v "50px");
`P (El.Style.position, Jstr.v "absolute");
`P (El.Style.background_color, Jstr.v "red");
]
in
Elwd.div ~st []
in
let playground =
Elwd.div
~st:
[
`P (El.Style.width, Jstr.v "1000px");
`P (El.Style.height, Jstr.v "1000px");
`P (El.Style.position, Jstr.v "relative");
]
[ `R block ]
in
Elwd.div [ `R set_top; `R set_left; `R playground ]

let () =
let ui = Lwd.observe ui in
let on_invalidate _ =
Console.(log [ str "on invalidate" ]);
let _ : int =
G.request_animation_frame @@ fun _ ->
let _ui = Lwd.quick_sample ui in
()
in
()
in
let on_load _ =
Console.(log [ str "onload" ]);
El.append_children (Document.body G.document) [ Lwd.quick_sample ui ];
Lwd.set_on_invalidate ui on_invalidate
in
ignore (Ev.listen Ev.dom_content_loaded on_load (Window.as_target G.window));
()
93 changes: 58 additions & 35 deletions lib/brr-lwd/elwd.ml
Original file line number Diff line number Diff line change
Expand Up @@ -177,32 +177,22 @@ let pure_unit = Lwd.pure ()

let dummy_kv_at = (Jstr.empty, Jstr.empty)

let attach_attribs el attribs =
let set_kv (k, v) =
if Jstr.equal k At.Name.class'
then El.set_class v true el
else El.set_at k (Some v) el
in
let unset_kv (k, v) =
if Jstr.equal k At.Name.class'
then El.set_class v false el
else El.set_at k None el
in
let set_lwd_at () =
let attach l set_kv unset_kv to_kv =
let set_lwd () =
let prev = ref dummy_kv_at in
fun at ->
fun x ->
if !prev != dummy_kv_at then
unset_kv !prev;
let pair = At.to_pair at in
set_kv pair;
prev := pair
let kv = to_kv x in
set_kv kv;
prev := kv
in
Lwd_utils.map_reduce (function
| `P _ -> assert false
| `R at -> Lwd.map ~f:(set_lwd_at ()) at
| `R at -> Lwd.map ~f:(set_lwd ()) at
| `S ats ->
let set_at' at =
let kv = At.to_pair at in
let set_at' x =
let kv = to_kv x in
set_kv kv;
kv
in
Expand All @@ -223,7 +213,29 @@ let attach_attribs el attribs =
in
Lwd.map ~f:update ats
) (pure_unit, Lwd.map2 ~f:(fun () () -> ()))
attribs
l

let attach_attribs el attribs =
let set_at (k,v) =
if Jstr.equal k At.Name.class'
then El.set_class v true el
else El.set_at k (Some v) el
in
let unset_at (k,v) =
if Jstr.equal k At.Name.class'
then El.set_class v false el
else El.set_at k None el
in
attach attribs set_at unset_at At.to_pair

let attach_style el styles =
let set_st (k,v) =
El.set_inline_style k v el
in
let unset_st (k,_) =
El.remove_inline_style k el
in
attach styles set_st unset_st Fun.id

let listen el (Handler {opts; type'; func}) =
Ev.listen ?opts type' func (El.as_target el)
Expand Down Expand Up @@ -260,24 +272,31 @@ let attach_events el events =
) (pure_unit, Lwd.map2 ~f:(fun () () -> ()))
events

let v ?d ?(at=[]) ?(ev=[]) tag children =
let v ?d ?(at=[]) ?(ev=[]) ?(st=[]) tag children =
let at, impure_at = prepare_col at in
let ev, impure_ev = prepare_col ev in
let st, impure_st = prepare_col st in
let children, impure_children = consume_children children in
let el = El.v ?d ~at tag children in
let result =
match impure_at, impure_children with
| [], None -> Lwd.pure el
| [], Some children ->
update_children el children
| at, None ->
Lwd.map ~f:(fun () -> el) (attach_attribs el at)
| at, Some children ->
Lwd.map2 ~f:(fun () el -> el)
(attach_attribs el at)
(update_children el children)
match impure_children with
| None -> Lwd.pure el
| Some children -> update_children el children
in
let result =
match impure_st with
| [] -> result
| st ->
Lwd.map2 ~f:(fun () el -> el) (attach_style el st) result
in
let result =
match impure_at with
| [] -> result
| at ->
Lwd.map2 ~f:(fun () el -> el) (attach_attribs el at) result
in
List.iter (fun h -> ignore (listen el h)) ev;
List.iter (fun (k,v) -> El.set_inline_style k v el) st;
let result =
match impure_ev with
| [] -> result
Expand All @@ -290,16 +309,20 @@ let v ?d ?(at=[]) ?(ev=[]) tag children =

(** {1:els Element constructors} *)

type cons = ?d:document -> ?at:At.t col -> ?ev:handler col -> t col -> t Lwd.t
type cons =
?d:document -> ?at:At.t col -> ?ev:handler col -> ?st:(El.Style.prop * Jstr.t) col ->
t col -> t Lwd.t
(** The type for element constructors. This is simply {!v} with a
pre-applied element name. *)

type void_cons = ?d:document -> ?at:At.t col -> ?ev:handler col -> unit -> t Lwd.t
type void_cons =
?d:document -> ?at:At.t col -> ?ev:handler col -> ?st:(El.Style.prop * Jstr.t) col ->
unit -> t Lwd.t
(** The type for void element constructors. This is simply {!v}
with a pre-applied element name and without children. *)

let cons name ?d ?at ?ev cs = v ?d ?at ?ev name cs
let void_cons name ?d ?at ?ev () = v ?d ?at ?ev name []
let cons name ?d ?at ?ev ?st cs = v ?d ?at ?ev ?st name cs
let void_cons name ?d ?at ?ev ?st () = v ?d ?at ?ev ?st name []

let a = cons Name.a
let abbr = cons Name.abbr
Expand Down
12 changes: 9 additions & 3 deletions lib/brr-lwd/elwd.mli
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ type 'a col = [
type handler (* An event handler *)
val handler : ?opts:Ev.listen_opts -> 'a Ev.type' -> ('a Ev.t -> unit) -> handler

val v : ?d:document -> ?at:At.t col -> ?ev:handler col -> tag_name -> t col -> t Lwd.t
val v :
?d:document -> ?at:At.t col -> ?ev:handler col -> ?st:(El.Style.prop * Jstr.t) col ->
tag_name -> t col -> t Lwd.t
(** [v ?d ?at name cs] is an element [name] with attribute [at]
(defaults to [[]]) and children [cs]. If [at] specifies an
attribute more thanonce, the last one takes over with the
Expand All @@ -26,11 +28,15 @@ val v : ?d:document -> ?at:At.t col -> ?ev:handler col -> tag_name -> t col -> t

(** {1:els Element constructors} *)

type cons = ?d:document -> ?at:At.t col -> ?ev:handler col -> t col -> t Lwd.t
type cons =
?d:document -> ?at:At.t col -> ?ev:handler col -> ?st:(El.Style.prop * Jstr.t) col ->
t col -> t Lwd.t
(** The type for element constructors. This is simply {!v} with a
pre-applied element name. *)

type void_cons = ?d:document -> ?at:At.t col -> ?ev:handler col -> unit -> t Lwd.t
type void_cons =
?d:document -> ?at:At.t col -> ?ev:handler col -> ?st:(El.Style.prop * Jstr.t) col ->
unit -> t Lwd.t
(** The type for void element constructors. This is simply {!v}
with a pre-applied element name and without children. *)

Expand Down