package bonsai
A library for building dynamic webapps, using Js_of_ocaml
Install
Dune Dependency
Authors
Maintainers
Sources
bonsai-v0.16.0.tar.gz
sha256=1d68aab713659951eba5b85f21d6f9382e0efa8579a02c3be65d9071c6e86303
doc/src/bonsai.web_ui_widget/bonsai_web_ui_widget.ml.html
Source file bonsai_web_ui_widget.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 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
open! Core open! Bonsai_web open! Js_of_ocaml open Bonsai.Let_syntax open Virtual_dom module Low_level = struct module Id = Unique_id.Int () let zero = Id.create () type 's t = { unsafe_init : 's -> Id.t ; unsafe_destroy : Id.t -> unit ; modify : ('s -> unit) -> unit Effect.t ; read : 'a. ('s -> 'a) -> 'a list Effect.t } module Model = struct type 's t = 's Id.Map.t let sexp_of_t = sexp_of_opaque let t_of_sexp = opaque_of_sexp let equal = phys_equal end module Action = struct type 's t = | Register of { id : Id.t ; state : 's } | Destroy of Id.t | Modify of ('s -> unit) let sexp_of_t = sexp_of_opaque end let component (type s) () = let module Model = struct include Model type nonrec t = s t end in let module Action = struct include Action type nonrec t = s t end in let%sub model, inject = Bonsai.state_machine0 (module Model) (module Action) ~reset:(fun ~inject:_ ~schedule_event:_ m -> m) ~default_model:Id.Map.empty ~apply_action:(fun ~inject:_ ~schedule_event:_ model -> function | Register { id; state } -> Map.set model ~key:id ~data:state | Destroy id -> Map.remove model id | Modify f -> Map.iter model ~f; model) in let%sub get_model = Bonsai.yoink model in let%arr inject = inject and get_model = get_model in let unsafe_init state = let id = Id.create () in Effect.Expert.handle_non_dom_event_exn (inject (Register { id; state })); id in let unsafe_destroy id = Effect.Expert.handle_non_dom_event_exn (inject (Destroy id)) in let modify f = inject (Modify f) in let read r = match%map.Effect get_model with | Inactive -> [] | Active m -> List.map (Map.data m) ~f:r in { unsafe_init; unsafe_destroy; modify; read } ;; end module State = struct type ('input, 'state) t = { mutable input : 'input ; mutable state : 'state ; mutable id : Low_level.Id.t ; get_input : unit -> 'input } end type ('input, 'state) reader = { f : 'a. ('input -> 'state -> 'a) -> 'a list Effect.t } type ('input, 'state) t = { view : Vdom.Node.t ; modify : ('input -> 'state -> unit) -> unit Effect.t ; read : 'a. ('input -> 'state -> 'a) -> 'a list Effect.t } module type S = sig type element = private #Dom_html.element type input type state val init : get_input:(unit -> input) -> input -> state * element Js.t val update : prev_input:input -> input -> state -> element Js.t -> element Js.t val destroy : input -> state -> element Js.t -> unit end let component (type input state) ?(vdom_for_testing = fun _ -> Vdom.Node.create "widget" []) (module M : S with type input = input and type state = state) input = let id = Type_equal.Id.create ~name:"widget" sexp_of_opaque in let%sub state_tracker = Low_level.component () in let%sub view = let%arr input = input and { unsafe_init; unsafe_destroy; _ } = state_tracker in Vdom.Node.widget ~vdom_for_testing:(lazy (vdom_for_testing input)) ~id ~init:(fun () -> let the_state = ref None in let get_input () = match !the_state with | None -> input | Some s -> s.State.input in let state, element = M.init ~get_input input in let s = { State.input; state; id = Low_level.zero; get_input } in the_state := Some s; let id = unsafe_init s in s.id <- id; s, element) ~update:(fun s element -> let { State.input = prev_input; state; get_input = _; id = _ } = s in if phys_equal input prev_input then s, element else ( s.input <- input; let element = M.update ~prev_input input state element in s, element)) ~destroy:(fun s element -> let { State.input; state; id; get_input = _ } = s in unsafe_destroy id; M.destroy input state element) () in let%sub funs = let%arr state_tracker = state_tracker in let modify f = state_tracker.modify (fun s -> f s.input s.state) in let reader = { f = (fun f -> state_tracker.read (fun s -> f s.input s.state)) } in modify, reader in let%arr view = view and modify, reader = funs in { view; modify; read = reader.f } ;;
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>