### OCaml: Counter Example with Bonsai Var Source: https://bonsai.red/02-dynamism Demonstrates a typical use case for Bonsai's 'Var.t'. It sets up a counter that increments every second and displays the current count. ```OCaml let counter_every_second : int Value.t = let counter_var : int Bonsai.Var.t = Bonsai.Var.create (-1) in every (Time_ns.Span.of_sec 1.0) (fun () -> Bonsai.Var.update counter_var ~f:(fun i -> i + 1)); Bonsai.Var.value counter_var ;; let view_for_counter : Vdom.Node.t Computation.t = let%arr counter = counter_every_second in Vdom.Node.textf "counter: %d" counter ;; ``` -------------------------------- ### Textbox Component using Bonsai.state Source: https://bonsai.red/03-state A practical example demonstrating the use of `Bonsai.state` to create a textbox component. It shows how to manage string state, link it to a VDOM input element, and handle input events to update the state. ```OCaml let textbox : (string * Vdom.Node.t) Computation.t = let%sub state, set_state = Bonsai.state (module String) ~default_model:"" in let%arr state = state and set_state = set_state in let view = Vdom.Node.input ~attr:Vdom.Attr.(value_prop state @ on_input (fun _ new_text -> set_state new_text)) () in state, view ;; ``` -------------------------------- ### Basic Textbox Usage with Placeholder (OCaml) Source: https://bonsai.red/07-flow Provides a simple example of using the textbox component with a statically defined placeholder value in OCaml. This illustrates the basic instantiation of the component. ```OCaml let textbox_with_placeholder = textbox ~placeholder:(Value.return "the placeholder") ``` -------------------------------- ### Implementing an Integer Textbox Form Source: https://bonsai.red/04-forms An example demonstrating how to implement a form for integer input using `Form.project`. It projects a string textbox form into an integer form using `Int.of_string` for parsing and `Int.to_string` for unparsing. ```OCaml let int_textbox : int Form.t Computation.t = let%sub form = Form.Elements.Textbox.string () in let%arr form = form in Form.project form ~parse_exn:Int.of_string ~unparse:Int.to_string;; ``` -------------------------------- ### Bulleted List Example Source: https://bonsai.red/01-virtual_dom Illustrates constructing a more complex virtual DOM structure representing an HTML bulleted list. It uses `div`, `h3`, `ul`, and `li` node constructors to create nested elements. ```OCaml let bulleted_list : Vdom.Node.t = let open Vdom.Node in div [ h3 [ text "Norwegian Pancakes" ] ; ul [ li [ text "3 eggs" ] ; li [ text "2 cups of milk" ] ; li [ text "1 cup of flour" ] ] ];; ``` -------------------------------- ### Textbox Value Extraction Example (OCaml) Source: https://bonsai.red/04-forms An example demonstrating how to extract the value from a textbox form element and display it as a S-expression for debugging purposes. It combines the form's view with its extracted value. ```OCaml let textbox_value = let%sub textbox = Form.Elements.Textbox.string () in let%arr textbox = textbox >>| Form.label "my textbox" in let value = Form.value textbox in Vdom.Node.div [ Form.view_as_vdom textbox ; Vdom.Node.sexp_for_debugging ([%sexp_of: string Or_error.t] value) ] ;; ``` -------------------------------- ### OCaml Form Value Setting Source: https://bonsai.red/04-forms Demonstrates how to programmatically set the value of a form element. This is useful for initializing forms or modifying them based on program logic. The example shows a textbox and a button that, when clicked, updates the textbox's value to 'hello world'. ```OCaml let form_set = let%sub textbox = Form.Elements.Textbox.string () in let%arr textbox = textbox >>| Form.label "my textbox" in Vdom.Node.div [ Form.view_as_vdom textbox ; Vdom.Node.button ~attr:(Vdom.Attr.on_click (fun _ -> Form.set textbox "hello world")) [ Vdom.Node.text "click me" ] ] ;; ``` -------------------------------- ### Multiple Counters Component Source: https://bonsai.red/07-flow An example of using Bonsai.assoc to create multiple counter components, one for each entry in an input map. The 'f' function maps each key-value pair to a 'counter_state_machine' computation. The output is a VDOM table displaying each counter. ```OCaml let multiple_counters (input : unit String.Map.t Value.t) = let%sub counters = Bonsai.assoc (module String) input ~f:(fun _key (_ : unit Value.t) -> State_examples.counter_state_machine) in let%arr counters = counters in Vdom.Node.table (counters |> Map.to_alist |> List.map ~f:(fun (key, vdom) -> let open Vdom.Node in let name = td [ Vdom.Node.text key ] in let counter = td [ vdom ] in Vdom.Node.tr [ name; counter ]));; ``` -------------------------------- ### Table Styling with Css_gen and Vdom.Attr.style Source: https://bonsai.red/08-css Provides examples of defining and applying various styles to HTML table elements using Css_gen and Vdom.Attr.style. This includes table borders, cell padding, header styles, and alternating row background colors. ```OCaml type row2 = { id : int; name : string; age : int; } let table_styles = let open Css_gen in border_collapse `Collapse @> border ~style:`Solid ~color:(`Name "brown") ~width:(`Px 1) () let thead_styles = let open Css_gen in text_align `Center @> background_color (`Name "brown") @> color (`Name "antiquewhite") @> font_weight `Bold let tr_odd = Css_gen.background_color (`Name "antiquewhite") let tr_even = Css_gen.background_color (`Name "wheat") let td_styles = Css_gen.padding ~top:(`Px 4) ~bottom:(`Px 4) ~left:(`Px 4) ~right:(`Px 4) () let basic_table_attr rows = let open Vdom.Node in let thead = thead ~attr:(Vdom.Attr.style thead_styles) [ td [ text "id" ]; td [ text "name" ]; td [ text "age" ] ] in let tbody = rows |> List.mapi ~f:(fun i { id; name; age } -> let tr_style = if Int.( % ) i 2 = 0 then tr_even else tr_odd in tr ~attr:(Vdom.Attr.style tr_style) [ td ~attr:(Vdom.Attr.style td_styles) [ textf "%d" id ] ; td ~attr:(Vdom.Attr.style td_styles) [ text name ] ; td ~attr:(Vdom.Attr.style td_styles) [ textf "%d" age ] ]) |> tbody in table ~attr:(Vdom.Attr.style table_styles) [ thead; tbody ] let politicians = basic_table_attr [ { id = 0; name = "George Washington"; age = 67 } ; { id = 1; name = "Alexander Hamilton"; age = 47 } ; { id = 2; name = "Abraham Lincoln"; age = 56 } ] ``` -------------------------------- ### OCaml: Bonsai Var API Source: https://bonsai.red/02-dynamism Defines the 'Var.t' type in Bonsai for dynamic data. It includes functions to create, update, set, get, and retrieve a value-tracking version of a variable. ```APIDOC type 'a t (** Creates a var with an initial value. *) val create : 'a -> 'a t (** Runs a function over the current value and updates it to the result. *) val update : 'a t -> f:('a -> 'a) -> unit (** Change the current value. *) val set : 'a t -> 'a -> unit (** Retrieve the current value. *) val get : 'a t -> 'a (** Get a value that tracks the current value, for use in a computation. *) val value : 'a t -> 'a Value.t ``` -------------------------------- ### Bonsai Chained on_change Computation Source: https://bonsai.red/09-edge-triggering Demonstrates a linear chain of Bonsai computations using `Bonsai.Edge.on_change`. This example shows how state changes propagate through a series of `on_change` callbacks, affecting subsequent states. It's designed to illustrate the step-by-step state updates that occur over multiple frames. ```OCaml let chain_computation = let%sub a = Bonsai.const "x" in let%sub b, set_b = Bonsai.state (module String) ~default_model:" " in let%sub c, set_c = Bonsai.state (module String) ~default_model:" " in let%sub d, set_d = Bonsai.state (module String) ~default_model:" " in let%sub () = Bonsai.Edge.on_change (module String) a ~callback:set_b in let%sub () = Bonsai.Edge.on_change (module String) b ~callback:set_c in let%sub () = Bonsai.Edge.on_change (module String) c ~callback:set_d in return (Value.map4 a b c d ~f:(sprintf "a:%s b:%s c:%s d:%s")) ``` -------------------------------- ### OCaml Variant Form Creation with Typed Variants Source: https://bonsai.red/04-forms Illustrates creating a Bonsai form for an OCaml variant type using `typed_variants`. This example shows how to define forms for each variant case, including those with associated data (int, string), and combine them into a single variant form. ```OCaml type v = | A | B of int | C of string [@@deriving typed_variants, sexp_of] let form_of_v : v Form.t Computation.t = Form.Typed.Variant.make (module struct module Typed_variant = Typed_variant_of_v let label_for_variant = `Inferred let form_for_variant : type a. a Typed_variant.t -> a Form.t Computation.t = function | A -> Bonsai.const (Form.return ()) | B -> Form.Elements.Textbox.int () | C -> Form.Elements.Textbox.string () ;; end) ``` -------------------------------- ### Instantiating Bonsai.state Source: https://bonsai.red/03-state Illustrates how to instantiate `Bonsai.state` for string data, obtaining the current state value and a function to update it. It highlights the use of `let%sub` and `let%arr` for accessing state values. ```OCaml let%sub state, set_state = Bonsai.state (module String) ~default_model:"" in ``` -------------------------------- ### Instantiating Multiple Counters with Bonsai Source: https://bonsai.red/02-dynamism Demonstrates how to create three independent counter buttons using Bonsai's `let%sub` and compose them into a single UI element using `Vdom.Node.div`. It also shows how `let%arr` is used to access the results within each counter's computation graph. ```OCaml let (three_counters : Vdom.Node.t Computation.t) = let%sub counter1 = counter_button in let%sub counter2 = counter_button in let%sub counter3 = counter_button in let%arr counter1 = counter1 and counter2 = counter2 and counter3 = counter3 in Vdom.Node.div [ counter1; counter2; counter3 ];; ``` -------------------------------- ### Hello World Text Node Source: https://bonsai.red/01-virtual_dom Demonstrates the creation of a simple text node using `Vdom.Node.text`. This is a fundamental building block for virtual DOM structures. ```OCaml let hello_world : Vdom.Node.t = Vdom.Node.text "hello world!" ``` -------------------------------- ### OCaml: Idiomatic computation using let%sub and let%arr Source: https://bonsai.red/02-dynamism Presents the idiomatic way to write Bonsai computations using let%sub for memoization and let%arr for defining dependencies, achieving efficient computation. ```OCaml let component (xs : int list Value.t) : string Computation.t = let%sub sum = let%arr xs = xs in List.fold xs ~init:0 ~f:( + ) in let%sub average = let%arr sum = sum and xs = xs in let length = List.length xs in if length = 0 then 0 else sum / length in return (let%map sum = sum and average = average in [%string "sum = %{sum#Int}, average = %{average#Int}"]) ``` -------------------------------- ### OCaml: Basic let%arr to let%map transformation Source: https://bonsai.red/02-dynamism Demonstrates how a computation using let%arr can be expanded to its equivalent form using let%map and return, illustrating the underlying mechanism. ```OCaml let f (x : int Value.t) : int Computation.t = let%arr x = x in x + 1 (* Expands to: *) let f (x : int Value.t) : int Computation.t = (let%arr x = x in x + 1) (* Which further expands to: *) let f (x : int Value.t) : int Computation.t = return (Value.map x ~f:(fun x -> x + 1)) ``` -------------------------------- ### Basic CSS Styling with Css_gen Source: https://bonsai.red/08-css Demonstrates creating basic CSS styles using Css_gen, such as text alignment and background color. It also shows how to combine multiple styles using the '@>' operator. ```OCaml let style: Css_gen.t = Css_gen.text_align `Center ``` ```OCaml let style: Css_gen.t = let open Css_gen in text_align `Center @> background_color (`Name "red") ``` -------------------------------- ### Comparison of CSS class application Source: https://bonsai.red/08-css Highlights the difference in applying CSS classes. The first line shows the previous method using a hardcoded string class name, while the second line demonstrates the updated approach using the `Style.politicians` module value generated by `ppx_css`. ```diff - table ~attr:(Vdom.Attr.class_ "politicians") [ thead; tbody ] + table ~attr:(Vdom.Attr.class_ Style.politicians) [ thead; tbody ] ``` -------------------------------- ### Many Frame Watches Component Source: https://bonsai.red/09-edge-triggering Creates a component that allows adding multiple frame counters. Each frame counter has its own state and starts counting from its creation. Clicking 'x' removes the component and stops its associated Bonsai computation graph and effects. ```OCaml let many_frame_watches = let%sub { contents; append; _ } = extendy frame_counter ~wrap_remove in let%arr contents = contents and append = append in let append_button = Vdom.Node.button ~attr:(Vdom.Attr.on_click (fun _ -> append)) [ Vdom.Node.text "add" ] in Vdom.Node.div (append_button :: Map.data contents) ;; ``` -------------------------------- ### Bonsai App UI Function Signature (OCaml) Source: https://bonsai.red/00-introduction Presents the signature for a Bonsai application's UI function. It shows how inputs are wrapped in Value.t and outputs in Computation.t, enabling incremental state machines for interactivity. ```OCaml open Bonsai_web val ui : Your_input_type_here.t Value.t -> Vdom.Node.t Computation.t ``` -------------------------------- ### OCaml Record Analogy Source: https://bonsai.red/08-css Compares component design in Bonsai Red to using records in OCaml, emphasizing the importance of defining fields and requiring their presence for constructing values. ```OCaml (* Analogy to OCaml records: component authors must define necessary fields *) (* Users constructing a value cannot omit fields they don't care about *) ``` -------------------------------- ### OCaml: Optimized computation using let%sub and return Source: https://bonsai.red/02-dynamism Shows how to prevent work duplication in Bonsai computations by wrapping let%map expressions within return and using let%sub to instantiate the computation. ```OCaml let component (xs : int list Value.t) : string Computation.t = let%sub sum = return (let%map xs = xs in List.fold xs ~init:0 ~f:( + )) in let%sub average = return (let%map sum = sum and xs = xs in let length = List.length xs in if length = 0 then 0 else sum / length) in return (let%map sum = sum and average = average in [%string "sum = %{sum#Int}, average = %{average#Int}"]) ``` -------------------------------- ### Basic UI Function Signature (OCaml) Source: https://bonsai.red/00-introduction Defines the basic signature for a user interface function in OCaml, where a Vdom.Node.t represents the application's view. This highlights the functional approach to UI development. ```OCaml (* Virtual_dom.Vdom.Node.t represents your applications view *) open Virtual_dom val ui : Your_input_type_here.t -> Vdom.Node.t ``` -------------------------------- ### OCaml Form Combination and Rendering Source: https://bonsai.red/04-forms Shows how to combine previously defined record and variant forms using `Form.both` and then render the combined form into a VDOM node. It also includes displaying the form's value for debugging purposes using `sexp_for_debugging`. ```OCaml let view_for_form : Vdom.Node.t Computation.t = let%sub form_t = form_of_t in let%sub form_v = form_of_v in let%arr form_t = form_t and form_v = form_v in let form = Form.both form_t form_v in let value = Form.value form in Vdom.Node.div [ Form.view_as_vdom form ; Vdom.Node.sexp_for_debugging ([%sexp_of: (t * v) Or_error.t] value) ] ``` -------------------------------- ### Bonsai State Machine with Buttons Source: https://bonsai.red/03-state This snippet demonstrates a basic Bonsai state machine that manages a counter. It defines an Action module for increment and decrement operations and uses VDOM buttons to trigger these actions via an injection function. ```OCaml module Action = struct type t = Increment | Decrement end let apply_action ~inject ~schedule_event model action = match action with | Increment -> model + 1 | Decrement -> model - 1 let%arr state = state and inject = inject in let decrement_button = Vdom.Node.button ~attr:(Vdom.Attr.on_click (fun _ -> inject Decrement)) [ Vdom.Node.text "-1" ] let increment_button = Vdom.Node.button ~attr:(Vdom.Attr.on_click (fun _ -> inject Increment)) [ Vdom.Node.text "+1" ] Vdom.Node.div [ decrement_button; Vdom.Node.textf "%d" state; increment_button ] ``` -------------------------------- ### Bonsai State Machine Initialization Source: https://bonsai.red/07-flow Initializes a Bonsai state machine using the defined Model and Action modules. It sets the default model and provides an apply_action function to handle state transitions based on actions. ```OCaml let people = Bonsai.state_machine0 (module Model) (module Action) ~default_model:Model.default ~apply_action:(fun ~inject:_ ~schedule_event:_ model action -> match action with | Add name -> Map.set model ~key:name ~data:() | Remove name -> Map.remove model name) ``` -------------------------------- ### Alternative Logging Syntax Source: https://bonsai.red/09-edge-triggering Demonstrates an alternative, more concise syntax for logging effects using function composition (`>>|`) and `Fn.( |> )`. ```OCaml log >>| Fn.( |> ) "🔥" ``` -------------------------------- ### Input with Placeholder Attribute Source: https://bonsai.red/01-virtual_dom Demonstrates how to add a placeholder attribute to an input element using Vdom.Attr.placeholder. ```OCaml let input_placeholder : Vdom.Node.t = Vdom.Node.input ~attr:(Vdom.Attr.placeholder "placeholder text here") () ;; ``` -------------------------------- ### Configure jbuild for ppx_css Source: https://bonsai.red/08-css This snippet shows how to add `ppx_css` to the `preprocess` section of your `jbuild` file to enable CSS preprocessing in your OCaml project. ```jbuild (executables ( (names (main)) (libraries (bonsai_web)) (preprocess (pps (ppx_jane ppx_css))) (js_of_ocaml ()))) ``` -------------------------------- ### OCaml: Creating Effects from Deferred Functions Source: https://bonsai.red/05-effect Demonstrates how to convert a function returning a Deferred.t into a function returning an Effect.t using Bonsai_web.Effect.of_deferred_fun. This is common for integrating RPCs into Bonsai applications. ```OCaml (* Pretend RPC function signature: *) val uppercase : string -> string Deferred.t (* Function to convert Deferred.t to Effect.t: *) val of_deferred_fun : ('query -> 'response Deferred.t) -> 'query -> 'response t (* Example usage: *) let uppercase_e : string -> string Effect.t = Bonsai_web.Effect.of_deferred_fun uppercase ``` -------------------------------- ### OCaml Form Rendering with Submission Options Source: https://bonsai.red/04-forms Renders a form into a VDOM node and provides options for handling form submission. The `on_submit` parameter allows for custom submission logic, including handling the enter key and adding a submit button with configurable text. The `f` field in `Submit.create` is called with the fully-validated form value. ```OCaml module Submit : sig type 'a t val create : ?handle_enter:bool -> ?button:string option -> f:('a -> unit Ui_effect.t) -> unit -> 'a t end val view_as_vdom : ?on_submit:'a Submit.t -> 'a t -> Vdom.Node.t ``` ```OCaml let textbox_on_submit = let%sub textbox = Form.Elements.Textbox.string () in let%arr textbox = textbox in textbox |> Form.label "text to alert" |> Form.view_as_vdom ~on_submit:(Form.Submit.create () ~f:alert) ;; ``` -------------------------------- ### OCaml: Computation with potential work duplication using let%map Source: https://bonsai.red/02-dynamism Illustrates a Bonsai computation where the 'sum' and 'average' values are computed using let%map, potentially leading to redundant calculations when both are used. ```OCaml let component (xs : int list Value.t) : string Computation.t = let sum = let%map xs = xs in List.fold xs ~init:0 ~f:( + ) in let average = let%map sum = sum and xs = xs in let length = List.length xs in if length = 0 then 0 else sum / length in let%arr sum = sum and average = average in [%string "sum = %{sum#Int}, average = %{average#Int}"] ``` -------------------------------- ### State Machine Counter Implementation Source: https://bonsai.red/03-state An OCaml implementation of a counter using Bonsai.state_machine0 to avoid race conditions. It defines an Action module with Increment and Decrement variants. ```OCaml module Action = struct type t = | Increment | Decrement [ @@deriving sexp_of] end let counter_state_machine : Vdom.Node.t Computation.t = let%sub state, inject = Bonsai.state_machine0 (module Int) ``` -------------------------------- ### Bonsai Library Overview Source: https://bonsai.red/index Bonsai is an OCaml library designed for building pure, incremental state-machines. It is particularly useful for web applications, enabling the composition of immutable virtual views and leveraging incrementality to optimize computations. The library's component abstraction allows for incrementalization of inputs and outputs, along with encapsulation of component-local state. ```OCaml (* Bonsai is an OCaml library for building and running pure, incremental, state-machines. *) (* It is primarily used as a foundational library for web applications, where immutable (virtual) representations of view are composed together with basic primitives. *) (* Incrementality is used to reduce unnecessarily recomputing parts of the application, and compared to other frameworks that have incrementality at the view-computation level, Bonsai can incrementalize any subcomputation. *) (* The "component" abstraction provided by Bonsai enables not only incrementalization of inputs and outputs, but also a powerful encapsulation of component-local state. *) ``` -------------------------------- ### Basic CSS Styling with ppx_css Source: https://bonsai.red/08-css Demonstrates the default behavior of ppx_css where identifiers are hashed. This is typically used when no specific identifier rewriting is needed. ```OCaml stylesheet {|.table-header {...}|} ``` -------------------------------- ### Button with Click Event Handler Source: https://bonsai.red/01-virtual_dom Illustrates how to attach a click event handler to a button using Vdom.Attr.on_click, which triggers an alert when clicked. ```OCaml let clicky : Vdom.Node.t = Vdom.Node.button ~attr: (Vdom.Attr.on_click (fun (_evt : mouse_event) -> alert "hello there!"; Ui_effect.Ignore)) [ Vdom.Node.text "click me!" ] ;; ``` -------------------------------- ### Counter Button Component - OCaml Source: https://bonsai.red/02-dynamism Creates a reusable UI component that displays a counter and a button to increment it. It utilizes Bonsai.state to manage the internal state of the counter. ```OCaml let counter_button : Vdom.Node.t Computation.t = let%sub count, set_count = Bonsai.state (module Int) ~default_model:0 in let%arr count = count and set_count = set_count in Vdom.Node.div [ Vdom.Node.text [%string "Counter value: %{count#Int}"] ; Vdom.Node.button ~attr:(Vdom.Attr.on_click (fun _ -> set_count (count + 1))) [ Vdom.Node.text "increment count" ] ];; ``` -------------------------------- ### OCaml Record Form Creation with Typed Fields Source: https://bonsai.red/04-forms Demonstrates creating a Bonsai form for an OCaml record type using the `typed_fields` ppx. It shows how to define forms for each field (string, int, bool) and combine them into a single record form. Requires the `typed_fields` ppx. ```OCaml type t = { some_string : string; an_int : int; on_or_off : bool; }[@@deriving typed_fields, sexp_of] let form_of_t : t Form.t Computation.t = Form.Typed.Record.make (module struct module Typed_field = Typed_field let label_for_field = `Inferred let form_for_field : type a. a Typed_field.t -> a Form.t Computation.t = function | Some_string -> Form.Elements.Textbox.string () | An_int -> Form.Elements.Number.int ~default:0 ~step:1 () | On_or_off -> Form.Elements.Checkbox.bool ~default:false () ;; end) ``` -------------------------------- ### Textbox Component with Dynamic Placeholder (OCaml) Source: https://bonsai.red/07-flow Defines a reusable textbox component in OCaml that accepts a dynamic placeholder. It utilizes Bonsai's state management and VDOM for rendering, allowing the placeholder to be updated based on external values. ```OCaml let textbox ~placeholder = let%sub state, set_state = Bonsai.state (module String) ~default_model:"" in (let%arr state = state and set_state = set_state and placeholder = placeholder in let view = Vdom.Node.input ~attr:(Vdom.Attr.many [ Vdom.Attr.value_prop state ; Vdom.Attr.on_input (fun _ new_text -> set_state new_text) ; Vdom.Attr.placeholder placeholder ]) () in state, view);; ``` -------------------------------- ### State-Based Counter Implementation Source: https://bonsai.red/03-state An OCaml implementation of a counter component using Bonsai.state. It includes buttons to increment and decrement the counter's value, displaying the current state. ```OCaml let state_based_counter : Vdom.Node.t Computation.t = let%sub state, set_state = Bonsai.state (module Int) ~default_model:0 in let%arr state = state and set_state = set_state in let decrement = Vdom.Node.button ~attr:(Vdom.Attr.on_click (fun _ -> set_state (state - 1))) [ Vdom.Node.text "-1" ] in let increment = Vdom.Node.button ~attr:(Vdom.Attr.on_click (fun _ -> set_state (state + 1))) [ Vdom.Node.text "+1" ] in Vdom.Node.div [ decrement; Vdom.Node.textf "%d" state; increment ];; ``` -------------------------------- ### Vdom Event Handler Signatures Source: https://bonsai.red/01-virtual_dom Provides the type signatures for common event handler attributes like on_click and on_input, detailing their parameters and return types. ```APIDOC val Vdom.Attr.on_click : (mouse_event -> unit Vdom.Effect.t) -> Vdom.Attr.t val Vdom.Attr.on_input : (input_event -> string -> unit Vdom.Effect.t) -> Vdom.Attr.t ``` -------------------------------- ### OCaml Dune Rule for CSS Concatenation Source: https://bonsai.red/08-css Demonstrates a Dune build rule to concatenate multiple CSS files into a single 'style.css' target, including project-specific styles and dependency stylesheets. ```ocaml (rule ( (targets (style.css)) (deps (%{root}/lib/dygraph/dist/dygraph.css ./my_styles.css)) (action "cat %{deps} > %{target}"))) ``` -------------------------------- ### Write CSS within OCaml using ppx_css Source: https://bonsai.red/08-css Demonstrates how to use the `[%css]` extension to embed CSS rules directly into an OCaml module. The ppx processes this CSS and generates a module (e.g., `Style`) with unique class names. ```ocaml module Style = [%css stylesheet {| table.politicians { border-collapse: collapse; border: 1px solid brown; } table.politicians td { padding: 4px; } table.politicians thead { text-align: center; background: brown; color: antiquewhite; font-weight: bold; } table.politicians tr { background: antiquewhite; } table.politicians tr:nth-child(even) { background: wheat; } |}] ``` -------------------------------- ### Add New Person Form Source: https://bonsai.red/07-flow Creates a form for adding a new person. It includes a textbox for the name, validation to ensure the name is not empty or whitespace, and a submit button that injects an 'Add' action. ```OCaml let add_new_person_form ~inject_add_person = let%sub form = Form.Elements.Textbox.string () in let%arr form = form and inject_add_person = inject_add_person in let on_submit name = Vdom.Effect.Many [ Form.set form ""; inject_add_person name ] in form |> Form.label "name" |> Form.validate ~f:(fun name -> if String.for_all name ~f:Char.is_whitespace then Error (Error.of_string "name must not be empty") else Ok ()) |> Form.view_as_vdom ~on_submit:(Form.Submit.create ~f:on_submit ()) ``` -------------------------------- ### OCaml: Effect vs Deferred Source: https://bonsai.red/05-effect Explains the core difference between OCaml's Effect.t and Deferred.t in the context of Bonsai's incremental computation model. Deferred.t executes side effects once, while Effect.t executes side effects each time it's bound. ```OCaml (* Theoretical difference: Deferred.t represents a future value, Effect.t represents the side effect itself. Practical difference: Deferred.t side effects once, Effect.t side effects multiple times when bound. *) (* Bonsai's model struggles with Deferred.t within Value.t, hence the preference for Effect.t *) ``` -------------------------------- ### Bonsai State Machine Type Signatures Source: https://bonsai.red/03-state This section contrasts the type signatures of `state_machine0` and `state_machine1`. It highlights how `state_machine1` incorporates an input value, making the state transition function dependent on external computations. ```APIDOC (* state_machine0 type signature *) val state_machine0 : Source_code_position.t -> (module Model with type t = 'model) -> (module Action with type t = 'action) -> default_model:'model -> apply_action: (inject:('action -> unit Effect.t) -> schedule_event:(unit Effect.t -> unit) -> 'model -> 'action -> 'model) Computation.t (* state_machine1 type signature *) val state_machine1 : Source_code_position.t -> (module Model with type t = 'model) -> (module Action with type t = 'action) -> default_model:'model -> apply_action: (inject:('action -> unit Effect.t) -> schedule_event:(unit Effect.t -> unit) -> 'input -> 'model -> 'action -> 'model) -> 'input Value.t -> ('model * ('action -> unit Effect.t)) Computation.t ``` -------------------------------- ### Chained Textbox Components (OCaml) Source: https://bonsai.red/07-flow Demonstrates chaining two textbox components in OCaml, where the output of the first textbox serves as the placeholder for the second. This showcases Bonsai's ability to manage complex data flows within a DAG structure. ```OCaml let textbox_chaining = let%sub a_contents, a_view = textbox ~placeholder:(Value.return "") in let%sub _, b_view = textbox ~placeholder:a_contents in let%arr a_view = a_view and b_view = b_view in let style = Vdom.Attr.style (Css_gen.display `Inline_grid) in Vdom.Node.div ~attr:style [ a_view; b_view ] ;; ``` -------------------------------- ### Bonsai.state Type Signature Source: https://bonsai.red/03-state Defines the type signature for the `Bonsai.state` function, outlining its parameters for module description and default model, and its return type which includes the state value and an update function. ```OCaml val state : (module Model with type t = 'model) -> default_model:'model -> ('model * ('model -> unit Effect.t)) Computation.t ``` -------------------------------- ### Basic Table Styling with CSS Source: https://bonsai.red/08-css This CSS code defines styles for HTML tables, including borders, padding, text alignment, and background colors for headers and rows. It demonstrates how to create a visually appealing table structure. ```CSS table { border-collapse: collapse; } table td { padding: 4px; } table thead { text-align: center; background: brown; color: antiquewhite; font-weight: bold; } table tr { background: antiquewhite; } table tr:nth-child(even) { background: wheat; } ``` -------------------------------- ### Juxtapose and Sum Computation - OCaml Source: https://bonsai.red/02-dynamism Combines the juxtapose_digits computation with a sum computation. It uses let%sub to create named sub-computations and then combines their results. ```OCaml let juxtapose_and_sum (a : int Value.t) (b : int Value.t) : string Computation.t = let%sub juxtaposed = juxtapose_digits ~delimiter:" + " a b in let%sub sum = let%arr a = a and b = b in Int.to_string (a + b) in let%arr juxtaposed = juxtaposed and sum = sum in juxtaposed ^ " = " ^ sum ;; ``` -------------------------------- ### Constant Input for Multiple Counters Source: https://bonsai.red/07-flow A constant input map used to demonstrate the multiple_counters component. It creates a map with two entries, 'hello' and 'there', each associated with a unit value. ```OCaml let multiple_counters_constant = multiple_counters ([ "hello", (); "there", () ] |> Map.of_alist_exn (module String) |> Value.return) ;; ``` -------------------------------- ### Rewriting Multiple Identifiers with ppx_css Source: https://bonsai.red/08-css Illustrates rewriting multiple identifiers simultaneously using the ~rewrite flag in ppx_css. This allows for granular control over identifier hashing for various CSS classes. ```OCaml stylesheet ~rewrite:[ "table-header", "table-header"; "table_row", "table-row" ] {|...|} ``` ```OCaml stylesheet ~rewrite:[ "my_table", My_table_component.table ] {|...|} ``` -------------------------------- ### Bonsai.Edge Module Overview Source: https://bonsai.red/09-edge-triggering The Bonsai.Edge module provides functions to observe changes in dependencies and schedule effects accordingly. It facilitates reacting to events such as the passage of time, component activation/deactivation, and changes in value contents. ```APIDOC Bonsai.Edge: Functions for observing transitions and scheduling effects. 1. Passage of time 2. Activation and deactivation of components 3. Changing of the contents of a Value.t Example usage involves scheduling Effects when these events occur. ``` -------------------------------- ### People Table Rendering Source: https://bonsai.red/07-flow Renders a table of people, displaying their names and associated counters. Each row includes a button to remove the person, which injects a 'Remove' action. ```OCaml let people_table people ~inject_remove_person = Bonsai.assoc (module String) people ~f:(fun name (_ : unit Value.t) -> let%sub counter = State_examples.counter_state_machine in let%arr counter = counter and name = name and inject_remove_person = inject_remove_person in let open Vdom.Node in let remove_person = td [ button ~attr:(Vdom.Attr.on_click (fun _ -> inject_remove_person name)) [ text "x" ] ] in let name = td [ text name ] in let counter = td [ counter ] in tr [ name; counter; remove_person ]) ``` -------------------------------- ### Conditional Textbox Rendering with match%sub Source: https://bonsai.red/07-flow This OCaml code snippet demonstrates how to use `match%sub` to conditionally render a second textbox. The second textbox is only displayed if the first textbox is not empty or does not contain only whitespace. It utilizes `Value.t` for input handling and `Computation.t` for the overall expression type. ```OCaml let textbox_matching = let%sub a_contents, a_view = textbox ~placeholder:(Value.return "") in let a_contents = let%map s = a_contents in let s = String.strip s in if String.is_empty s then None else Some s in match%sub a_contents with | None -> let%arr a_view = a_view in let message = Vdom.Node.div [ Vdom.Node.text "" ] in Vdom.Node.div [ a_view; message ] | Some placeholder -> let%sub _, b_view = textbox ~placeholder in let%arr a_view = a_view and b_view = b_view in let style = Vdom.Attr.style (Css_gen.display `Inline_grid) in Vdom.Node.div ~attr:style [ a_view; b_view ] ;; ``` -------------------------------- ### Form Operations (OCaml) Source: https://bonsai.red/04-forms Details the three primary operations for a `'a Form.t`: extracting its value, computing its VDOM view, and setting its value. ```OCaml val Form.value : 'a Form.t -> 'a Or_error.t ``` ```OCaml val Form.view_as_vdom : 'a Form.t -> Vdom.Node.t ``` ```OCaml val Form.set : 'a Form.t -> 'a -> unit Vdom.Effect.t ``` -------------------------------- ### Kudo Tracker Component Source: https://bonsai.red/07-flow Combines the state machine, add person form, and people table into a single UI component. It manages the state for people and handles actions for adding and removing them. ```OCaml let kudo_tracker = let%sub people, inject_action = people in let%sub form = let inject_add_person = let%map inject_action = inject_action in fun name -> inject_action (Add name) in add_new_person_form ~inject_add_person in (* The rest of the kudo_tracker component would typically go here, combining the form and the people table. *) ``` -------------------------------- ### OCaml Css_gen Library for Styles Source: https://bonsai.red/08-css Highlights a limitation in the OCaml `Css_gen` library where many CSS attributes are missing. It suggests a workaround using `Css_gen.create` to define custom styles when the library does not provide the necessary attributes. ```OCaml (* Example of using Css_gen.create for a missing attribute *) let custom_style = Css_gen.create "missing-attribute" "value";; (* Applying the custom style to an element *) let element_with_custom_style = Html.create_element ~style:custom_style "div";; ``` -------------------------------- ### Testing Chained on_change with Handle.show Source: https://bonsai.red/09-edge-triggering Unit test for the `chain_computation` using `Handle.show` to step through each frame. This test verifies the sequential state updates triggered by `on_change` callbacks, showing how the output string evolves over multiple calls to `Handle.show` as the states settle. ```OCaml let%expect_test "chained on_change" = let handle = Handle.create (Result_spec.string (module String)) chain_computation in Handle.show handle; [%expect {| a:x b: c: d: |}]; Handle.show handle; [%expect {| a:x b:x c: d: |}]; Handle.show handle; [%expect {| a:x b:x c:x d: |}]; Handle.show handle; [%expect {| a:x b:x c:x d:x |}]; Handle.show handle; [%expect {| a:x b:x c:x d:x |}] ``` -------------------------------- ### Form Module Alias Source: https://bonsai.red/04-forms Defines a module alias for the bonsai_web_ui_form library for easier use within the documentation. ```OCaml module Form = Bonsai_web_ui_form ``` -------------------------------- ### CSS Selector Specificity Source: https://bonsai.red/08-css Illustrates the issue of overly general CSS selectors and demonstrates how to write more specific selectors to avoid unintended style overrides. ```CSS /* Overly general selector */ .table.politicians td { /* styles */ } ``` ```CSS /* More specific selector */ table.politicians > tbody > tr > td { /* styles */ } ``` -------------------------------- ### Uppercase Effect Handler (Initial) Source: https://bonsai.red/05-effect The initial implementation of the `on_submit` handler within a Bonsai component. It takes string input, binds to the result of an `uppercase_e` effect, and updates the component's state with the filled result. ```OCaml let on_submit (contents : string) : unit Effect.t = let%bind.Effect s = uppercase_e contents in set_result (Filled s) ```