### Starting a Bonsai Web Application Source: https://github.com/janestreet/bonsai_web/blob/master/docs/how_to/bonsai_runtime.md This is the entry point for a Bonsai web application, typically defined in your main.ml file. It starts the application by rendering a specified component. ```ocaml let () = Bonsai_web.Start.start My_app.component ``` -------------------------------- ### Bonsai Computation Graph Construction Example Source: https://github.com/janestreet/bonsai_web/blob/master/docs/advanced/how_bonsai_works.md Illustrates the step-by-step construction of the Bonsai computation graph using 'Bonsai.state' and how the 'graph' variable is updated. The 'graph' starts as an identity function and is progressively wrapped in 'Sub' nodes. ```ocaml let computation graph = let model1, _inject = Bonsai.state 5 graph in let model2, _inject = Bonsai.state 6 graph in model2 ``` -------------------------------- ### Prepare Computations for Startup Time Comparison Source: https://github.com/janestreet/bonsai_web/blob/master/docs/how_to/benchmarking.md Wrap and name the computations, providing initial input values for comparison. This setup is required for Bonsai_bench.compare_startup. ```ocaml let computations = let wrap f a_b (local_ _graph) = let%sub a, b = a_b in f a b in [ "f1", wrap f1; "f2", wrap f2 ] ;; let startup_inputs = [ "same", (2, 2); "different", (1, 2) ] let startup_set : Bonsai_bench.Benchmark_set.t = Bonsai_bench.compare_startup ~name:"Startup: f1 vs f2" ~computations startup_inputs ;; ``` -------------------------------- ### Bonsai Call Function Example Source: https://github.com/janestreet/bonsai_web/blob/master/docs/blog/history.md Provides an example of using the 'call' function, which likely applies a function (arrow) to a value, and demonstrates its usage with 'bind' and pattern matching for extracting results. ```ocaml let call arrow value = let%bind result = value >>> arrow in result bind (value >>> arrow) ~f:(fun r -> r) let a = call arrow x ``` -------------------------------- ### Code Example Explanation Source: https://github.com/janestreet/bonsai_web/blob/master/docs/public_garden_exports/thinking_in_bonsai.md This comment clarifies which parts of the provided OCaml code are components and which are views, based on the definitions discussed. ```ocaml {|`Page` and `Counter` are both components. `Increment_button` is a view|} ``` -------------------------------- ### Hand-rolled Order Form Example Source: https://github.com/janestreet/bonsai_web/blob/master/docs/how_to/forms.md An example of a hand-rolled form using plain Bonsai primitives for price and size input, demonstrating manual state management and view composition. ```ocaml let order_form (local_ graph) = let price, set_price = Bonsai.state_opt graph in let size, set_size = Bonsai.state_opt graph in let value = let%arr price and size in { Order.price = Option.value price ~default:0. ; size = Option.value size ~default:0 } in let view = let%arr price and set_price and size and set_size in {%html| Price ) > Size ) > |} in value, view ;; ``` -------------------------------- ### Bonsai Component Input Example Source: https://github.com/janestreet/bonsai_web/blob/master/docs/blog/history.md Illustrates a common pattern before the 'input type parameter, where components closed over additional parameters for input. ```ocaml let my_component something_to_close_over = ... construct a component here ... ``` -------------------------------- ### Main Routing Component Example Source: https://github.com/janestreet/bonsai_web/blob/master/docs/public_garden_exports/thinking_in_bonsai.md The main Bonsai component demonstrating routing. It creates a Url_var, matches the current route to render specific page content, and displays navigation and the current URL. ```ocaml open Uri_parsing module Route = struct type t = | Homepage [@index] | Post of int | Search of { query : string } [@@deriving typed_variants, sexp, equal, uri_parsing] end module Navigation_buttons = struct let component ~set_uri () = let%arr set_uri in {%html.jsx|
|} ;; end let component (local_ graph) = let current_route, set_uri = Url_helper.create (module Route) graph in let page_content = match%sub current_route with | Route.Homepage -> Bonsai.return Home_page.view | Post post_id -> let%arr post_id in Post_page.view ~post_id | Search { query } -> let%arr query in Search_page.view ~query in let string_of_route = unstage (Parser.to_string Route.parser) in let%arr current_url_display = Url_display.component ~current_route ~string_of_route () and nav_buttons = Navigation_buttons.component ~set_uri () and page_content in {%html.jsx|

URL-Based Routing Demo

%{current_url_display} %{nav_buttons} %{page_content}
|} ;; ``` -------------------------------- ### Composing Date Pickers with Bonsai.Proc Source: https://github.com/janestreet/bonsai_web/blob/master/docs/blog/proc.md This example shows the equivalent composition of date pickers using the new Bonsai.Proc API. It illustrates improved readability by using `let%sub` and `return` for clearer data flow and composition. ```ocaml open Bonsai.Proc.Let_syntax let date_picker = let%sub year_picker = year_picker (Value.return ()) in let year = Value.map year_picker ~f:(fun (_, year) -> year) in let%sub month_picker = month_picker year in let month = Value.map month_picker ~f:(fun (_, month) -> month) in let%sub day_picker = day_picker year month in return ( let%map year_view, year = year_picker and month_view, month = month_picker and day_view, day = day_picker in let view = Vdom.Node.div [] [year_view; month_view; day_view] in view, year, month, day) ``` -------------------------------- ### Wormhole Usage Example with Multiple Mappings Source: https://github.com/janestreet/bonsai_web/blob/master/docs/blog/history.md An example demonstrating the 'wormhole' combinator's usage. It shows how to define a component that reads multiple values and associates them using Bonsai.Map.assoc, leveraging the 'read' parameter provided by wormhole. ```ocaml wormhole (fun ~read -> let%map a = ... and b = ... and c = ... and d = Bonsai.Map.assoc (module Int) (Bonsai.both (...) read) ) ``` -------------------------------- ### Create Versioned Parsers Source: https://github.com/janestreet/bonsai_web/blob/master/docs/how_to/uri_parsing.md Demonstrates how to create a versioned parser, starting with the first version based on an old parser and then defining a new version with updated URL structures and a mapping function. ```ocaml let v1_parser = Versioned_parser.first_parser old_parser let v2_parser = Versioned_parser.new_parser parser ~previous:v1_parser ~f:(function | Homepage -> Homepage | Post id -> Discussion id | Search query -> Search { query; author_id = None; categories = [] }) ;; ``` -------------------------------- ### Simplified Bonsai Signature in Bonsai Beta Source: https://github.com/janestreet/bonsai_web/blob/master/docs/blog/history.md This example shows the simplified Bonsai signature in Bonsai Beta, achieved by hiding the 'action type parameter. ```ocaml module Bonsai : sig type ('model, 'result) t val both : ('model, 'result_1) t -> ('model, 'result_2) t -> ('model, 'result_1 * 'result_2) t val return : 'result -> (_, 'result) t val map : ('model, 'r1) t -> f:('r1 -> 'r2) -> ('model, 'r2) t module Let_syntax : sig ... end end ``` -------------------------------- ### Per-Key State Management with Bonsai.scope_model Source: https://github.com/janestreet/bonsai_web/blob/master/docs/public_garden_exports/quick_start.md Demonstrates how to maintain independent state for different keys using Bonsai.scope_model. Each unique key gets its own state that persists across switches. ```ocaml module Form = Bonsai_web_form.With_manual_view module Counter = struct let component (graph @ local) = let count, set_count = Bonsai.state' 0 graph in let%arr count and set_count in {%html.jsx|
#{" Count: "}#{Int.to_string count}
|} ;; end module Scoped_counters = struct let component (graph @ local) = let form = Form.Elements.Dropdown.list (module String) ~equal:[%equal: String.t] (Bonsai.return [ "Alice"; "Bob"; "Charlie" ]) graph in let active_user = let%arr form in Form.value_or_default form ~default:"Alice" in Bonsai.scope_model (module String) ~on:active_user graph ~for_:(fun graph -> let%arr counter = Counter.component graph and form in {%html.jsx|
User: %{Form.view form}
%{counter}
|}) ;; end let component = Scoped_counters.component graph ``` -------------------------------- ### Testing Dynamic User Input with Bonsai.Var Source: https://github.com/janestreet/bonsai_web/blob/master/docs/how_to/testing.md Shows how to test components that accept dynamic inputs using Bonsai.Expert.Var. The example demonstrates creating a mutable variable, updating its value, and re-rendering the component to observe changes. ```ocaml let hello_user (name : string Bonsai.t) (local_ _graph) : Vdom.Node.t Bonsai.t = let%arr name in Vdom.Node.span [ Vdom.Node.textf "hello %s" name ] ;; ``` ```ocaml let%expect_test "shows hello to a user" = let user_var = Bonsai.Expert.Var.create "Bob" in let user = Bonsai.Expert.Var.value user_var in let handle = Handle.create (Result_spec.vdom Fn.id) (hello_user user) in Handle.show handle; [%expect {| hello Bob }]; Bonsai.Expert.Var.set user_var "Alice"; [%expect {| |}]; Handle.show handle; [%expect {| hello Alice |}] ;; ``` -------------------------------- ### Composing Date Pickers with Arrow Combinators Source: https://github.com/janestreet/bonsai_web/blob/master/docs/blog/proc.md This example demonstrates how to compose three Bonsai components (year, month, day pickers) into a single date picker using the older Arrow combinators. It highlights the complexity of managing intermediate values. ```ocaml val year_picker: (unit, Vdom.Node.t * Year.t ) Bonsai.t val month_picker: (Year.t, Vdom.Node.t * Month.t) Bonsai.t val day_picker: (Year.t * Month.t, Vdom.Node.t * Day.t ) Bonsai.t ``` ```ocaml val date_picker: (unit, Vdom.Node.t * Year.t * Month.t * Day.t) Bonsai.t ``` ```ocaml open Bonsai.Arrow let date_picker = year_picker >>| (fun (year_view, year) -> (year_view, year), year) |> second month_picker >>| (fun ((year_view, year), (month_view, month) -> (year_view, month_view, year, month), (year, month) |> second day_picker >>| (fun ((year_view, month_view, year, month), (day_view, day)) -> let view = Vdom.Node.div [] [year_view; month_view; day_view] in view, year, month, day) ``` -------------------------------- ### Implementing Form.Elements.Textbox.int using Form.project Source: https://github.com/janestreet/bonsai_web/blob/master/docs/how_to/forms.md This example demonstrates how `Form.project` is used to create a form for integers by projecting from a string form. Non-integer input will result in an error. ```ocaml let int (local_ graph) : (int, Vdom.Node.t) Form.t Bonsai.t = let form = Form.Elements.Textbox.string ~allow_updates_when_focused:`Always () graph in let%arr form in Form.project form ~parse_exn:Int.of_string ~unparse:Int.to_string ;; ``` -------------------------------- ### Bonsai Component Duplication Example Source: https://github.com/janestreet/bonsai_web/blob/master/docs/blog/proc.md Illustrates how two uses of a Bonsai component 'c' result in separate computations, similar to calling a regular function twice. ```ocaml val c : (input, result) Bonsai.t let r = let%map a = c >>| foo and b = c >>| bar in ... ``` -------------------------------- ### OCaml: Reusable Component Structure with ppx_html Source: https://github.com/janestreet/bonsai_web/blob/master/docs/public_garden_exports/quick_start.md Structure reusable UI components as modules with a `view` function. This example shows a `Greeting` component and how it's used in a `Home_page` component. ```ocaml module Greeting = struct let view () = {%html.jsx|
Hello, world!
|} end module Home_page = struct let view () = {%html.jsx||} end ``` -------------------------------- ### Combining UI Components Source: https://github.com/janestreet/bonsai_web/blob/master/docs/how_to/resetting_state.md Shows how to combine the connection status UI with other components, like counters, into a single UI element. This setup can lead to unintended resets of the connection status if not handled properly. ```ocaml let connection_and_counters (local_ graph) = let%arr connection_status_ui = connection_status_ui graph and counters = two_counters graph in Vdom.Node.div [ connection_status_ui; counters ] let resettable_ui = reset_ui ~f:connection_and_counters ``` -------------------------------- ### Future Date Picker Composition with Bonsai.Proc (Pattern Destructuring) Source: https://github.com/janestreet/bonsai_web/blob/master/docs/blog/proc.md This example demonstrates a future syntax for Bonsai.Proc that allows pattern destructuring directly in `let%sub` bindings. This further simplifies the composition of date pickers by reducing boilerplate. ```ocaml open Bonsai.Proc.Let_syntax let date_picker = let%sub year_view, year = year_picker in let%sub month_view, month = month_picker year in let%sub day_view, day = day_picker year month return ( let%map year_view = year_view and month_view = month_view and day_view = day_view and year = year and month = month and day = day in let view = Vdom.Node.div [] [year_view; month_view; day_view] in view, year, month, day) ``` -------------------------------- ### App Rendering Widget Using Diff Patch Source: https://github.com/janestreet/bonsai_web/blob/master/docs/how_to/low_level_vdom.md An example Bonsai application that utilizes the `widget_using_diff_patch`. It displays click counts and patch counts, demonstrating how the widget can interact with the application's state. ```ocaml let app (local_ graph) = let num_clicks, update_num_clicks = Bonsai.state' 0 graph in let num_patches, set_num_patches = Bonsai.state 0 graph in let view = let%arr num_patches and num_clicks in {%html.jsx|

Clicks: %{num_clicks#Int}

Patches: %{num_patches#Int}

|} in let%arr view and set_num_patches and update_num_clicks in (* NOTE: If we used this widget more than once, the separate widget instances would be ``` -------------------------------- ### Combining Forms with Form.both and Form.map_view Source: https://github.com/janestreet/bonsai_web/blob/master/docs/how_to/forms.md This example shows how to use `Form.both` to combine multiple input forms (int, float, string, bool) into a single form and then uses `Form.map_view` to construct a custom VDOM view for the combined form. ```ocaml let big_tuple_form (local_ graph) : ((int * float) * (string * bool), Vdom.Node.t) Form.t Bonsai.t = let int_and_float = let%arr int = Form.Elements.Textbox.int ~allow_updates_when_focused:`Always () graph and float = Form.Elements.Textbox.float ~allow_updates_when_focused:`Always () graph in Form.both int float in let string_and_bool = let%arr string = Form.Elements.Textbox.string ~allow_updates_when_focused:`Always () graph and bool = Form.Elements.Checkbox.bool ~default:false () graph in Form.both string bool in let%arr int_and_float and string_and_bool in Form.both int_and_float string_and_bool |> Form.map_view ~f:(fun ((int_view, float_view), (string_view, bool_view)) -> View.vbox [ View.hbox ~gap:(`Px 5) [ Vdom.Node.text "an int: "; int_view; Vdom.Node.text "a float: "; float_view ] ; View.hbox ~gap:(`Px 5) [ Vdom.Node.text "a string: " ; string_view ; Vdom.Node.text "a bool: " ; bool_view ] ]) ;; ``` -------------------------------- ### Create Startup Benchmark Source: https://github.com/janestreet/bonsai_web/blob/master/docs/how_to/benchmarking.md Use `Bonsai_bench.create_for_startup` to benchmark the startup time of a Bonsai application. The `~name` parameter is required. ```ocaml let app_startup_bench : Bonsai_bench.t = Bonsai_bench.create_for_startup ~name:"app startup" my_app ;; ``` -------------------------------- ### Multiple Counters with Bonsai.assoc Source: https://github.com/janestreet/bonsai_web/blob/master/docs/how_to/state_per_key.md Demonstrates how to create and display a separate counter for each user using Bonsai.assoc. This approach shows all counters simultaneously. ```ocaml let counters_for_users_assoc (local_ graph) : Vdom.Node.t Bonsai.t = let users = [ "Alice", (); "Bob", (); "Charlie", () ] |> String.Map.of_alist_exn |> Bonsai.return in let counters = Bonsai.assoc (module String) users ~f:(fun _ _ graph -> State_examples.counter_ui graph) graph in let%arr 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 ])) ;; ``` -------------------------------- ### match%map Usage Source: https://github.com/janestreet/bonsai_web/blob/master/docs/upgrade/local-graph.md The `match%map` construct is generally safe to use, as demonstrated in this example. ```ocaml let component = match%map ... with | ... -> ... ``` -------------------------------- ### Sign Git Commit Source: https://github.com/janestreet/bonsai_web/blob/master/CONTRIBUTING.md Example of how to sign a Git commit message with your name and email. This is required for all contributions to the project. ```git Signed-off-by: Joe Smith ``` -------------------------------- ### Basic VDOM vs Bonsai.View Button Source: https://github.com/janestreet/bonsai_web/blob/master/docs/how_to/theming.md Compares the VDOM API for creating a button with the simplified Bonsai.View button helper. Demonstrates the use of View.button with theme and on_click. ```ocaml open Virtual_dom let button = Vdom.Node.button ~attrs:[ Vdom.Attr.on_click (fun _ -> do_thing) ] [ Vdom.Node.text "click me" ] ;; (* vs *) let button' = View.button theme ~on_click:do_thing "click me" ``` -------------------------------- ### Run Benchmark Sets via Command Line Source: https://github.com/janestreet/bonsai_web/blob/master/docs/how_to/benchmarking.md Combine multiple benchmark sets (startup, interaction, etc.) and run them using Bonsai_bench.run_sets_via_command. This function handles the execution and display of results. ```ocaml let () = Bonsai_bench.run_sets_via_command [ startup_set ; interaction_set ; Bonsai_bench.set ~name:"list of things" list_of_things_bench ] ;; ``` -------------------------------- ### Url_var.t API Source: https://github.com/janestreet/bonsai_web/blob/master/docs/how_to/url_var.md Provides the core functions for interacting with Url_var.t, including getting the current value and setting new values with effects. ```ocaml val value : 'a Url_var.t -> 'a Bonsai.t val set_effect : ?how:[ `Push | `Replace ] -> 'a Url_var.t -> 'a -> unit Effect.t ``` -------------------------------- ### Run Benchmarks via Command Source: https://github.com/janestreet/bonsai_web/blob/master/docs/how_to/benchmarking.md Execute a list of Bonsai benchmarks using `Bonsai_bench.run_via_command`. Ensure only one `run_via_command` or `run_sets_via_command` is used per file. ```ocaml let () = Bonsai_bench.run_via_command ([ app_startup_bench ] @ list_of_things_bench @ [ state_bench ]) ;; ``` -------------------------------- ### Bonsai State Initialization Source: https://github.com/janestreet/bonsai_web/blob/master/docs/how_to/organizing_state.md Demonstrates two ways to initialize state for a component: internally using Bonsai.state or externally by accepting state and a setter as parameters. This allows for flexible component design, supporting both uncontrolled and controlled behaviors. ```OCaml let state, set_state = Bonsai.state "" graph ``` ```OCaml ?state:'state Bonsai.t * ('state -> unit Effect.t) Bonsai.t ``` -------------------------------- ### Exploded 'action Type Parameter in Bonsai Source: https://github.com/janestreet/bonsai_web/blob/master/docs/blog/history.md This example illustrates the unwieldy 'action type parameter that resulted from component composition in the original Bonsai. ```ocaml (never_returns, (never_returns, (never_returns, (never_returns, (never_returns, ((Interaction.Action.t, (int Model_editor.Action.t, Code_slide.Action.t) Either.t) Either.t, ((Interaction.Action.t, (int Model_editor.Action.t, Code_slide.Action.t) Either.t) Either.t, (((Interaction.Action.t, Interaction.Action.t) Either.t, (Inner_model.t Model_editor.Action.t, Code_slide.Action.t) Either.t) Either.t, ((string * Interaction.Action.t, (int Core_kernel.String.Map.t Model_editor.Action.t, Code_slide.Action.t) Either.t) Either.t, ((never_returns, (never_returns, Code_slide.Action.t) Either.t) Either.t, ((never_returns, (never_returns, Code_slide.Action.t) Either.t) Either.t, ((never_returns, (never_returns, Code_slide.Action.t) Either.t) Either.t, ((Interaction.Action.t, (int Model_editor.Action.t, Code_slide.Action.t) Either.t) Either.t, (((Interaction.Action.t, Interaction.Action.t) Either.t, (Inner_model.t Model_editor.Action.t, Code_slide.Action.t) Either.t) Either.t, ((string * Interaction.Action.t, (int Core_kernel.String.Map.t Model_editor.Action.t, Code_slide.Action.t) Either.t) Either.t, ((never_returns, (never_returns, Code_slide.Action.t) Either.t) Either.t, (((string * Interaction.Action.t) Bonsai_timetravel_example.Time_travel.Action.t, (int Core_kernel.String.Map.t Model_editor.Action.t, Code_slide.Action.t) Either.t) Either.t, (never_returns, (never_returns, never_returns) Either.t) Either.t) Either.t) Either.t) Either.t) Either.t) Either.t) Either.t) Either.t) Either.t) Either.t) Either.t) Either.t) Either.t) Either.t) Either.t) Either.t) Either.t) Either.t ``` -------------------------------- ### Exchange Interface Definition Source: https://github.com/janestreet/bonsai_web/blob/master/docs/how_to/resetting_state.md Defines the interface for interacting with an exchange, including order management and subscription to events. This is a prerequisite for the order manager example. ```ocaml module Exchange : sig module Order_id = Int (* A connection to an exchange *) type t type event = Fill of Order_id.t val create : unit -> t (* Sends an order to the exchange *) val send_order : t -> Order_id.t -> unit Effect.t (* Cancels an open order on the exchange *) val cancel_order : t -> Order_id.t -> unit Effect.t (* Subscribe to notifications of which orders have been filled *) val subscribe : t -> (event -> unit Effect.t) -> unit Effect.t end ``` -------------------------------- ### State and Resetter with Bonsai.with_model_resetter Source: https://github.com/janestreet/bonsai_web/blob/master/docs/how_to/best_practices_pitfalls.md Demonstrates how to use Bonsai.with_model_resetter to manage state and provide a reset mechanism. It shows how to combine separate Bonsai computations using Bonsai.both and split them later with let%sub. ```ocaml let state_with_resetter ~default_value (local_ graph) : int Bonsai.t * (int -> unit Effect.t) Bonsai.t * unit Effect.t Bonsai.t = let state_and_setter, reset = Bonsai.with_model_resetter ~f:(fun (local_ graph) -> let state, set_state = Bonsai.state default_value graph in Bonsai.both state set_state) graph in let%sub (state : int Bonsai.t), (set_state : (int -> unit Effect.t) Bonsai.t) = (state_and_setter : (int * (int -> unit Effect.t)) Bonsai.t) in state, set_state, reset ;; ``` -------------------------------- ### OCaml: Rendering Components with Expression Syntax (<%{expression}>) using ppx_html Source: https://github.com/janestreet/bonsai_web/blob/master/docs/public_garden_exports/quick_start.md Shows how to render components using the general `<%{expression}>` syntax, which allows any OCaml expression and supports positional arguments. ```ocaml (* Note the positional `header` argument *) let card (header : string) ?(attrs = []) children = {%html.jsx|

#{header}

*{children}
|} ;; let view = {%html.jsx| <%{card "Hello, world!"} class="greeting" >#{" Card content here "} |} ;; ``` -------------------------------- ### Increment Button View Source: https://github.com/janestreet/bonsai_web/blob/master/docs/public_garden_exports/thinking_in_bonsai.md An example of a Bonsai view that takes a function to update a counter and returns an HTML button. Views do not require `let%arr`. ```ocaml module Increment_button = struct let view ~set_counter () = {%html.jsx| |} ;; end ``` -------------------------------- ### Example Usage of Updated Tabs API Source: https://github.com/janestreet/bonsai_web/blob/master/docs/blog/history.md Demonstrates how to use the updated 'tabs' API with a custom enum type 'My_tabs' and specialized components for each tab. ```ocaml module My_tabs = struct type t = A | B | C [@@deriving sexp, enumerate] end tabs (module My_tabs) ~specialize:(fun set_tab -> function | A -> a_component | B -> b_component | C -> build_c_component set_tab) ``` -------------------------------- ### Basic VDOM vs Bonsai.View Flex Container Source: https://github.com/janestreet/bonsai_web/blob/master/docs/how_to/theming.md Compares the low-level VDOM API for creating a flex container with the simplified Bonsai.View API. Shows how View.hbox_wrap simplifies flexbox layout. ```ocaml let text_a = Vdom.Node.span [ Vdom.Node.text "A" ] let text_b = Vdom.Node.span [ Vdom.Node.text "B" ] let text_c = Vdom.Node.span [ Vdom.Node.text "C" ] let flex_container = Vdom.Node.div ~attrs: [ {%css| flex-direction: row; flex-wrap: wrap; |} ] [ text_a; text_b; text_c ] ;; (* vs *) let text_a', text_b', text_c' = View.text "A", View.text "B", View.text "C" let flex_container' = View.hbox_wrap [ text_a'; text_b'; text_c' ] ``` -------------------------------- ### Textbox Form Element Example Source: https://github.com/janestreet/bonsai_web/blob/master/docs/how_to/forms.md Demonstrates how to create a textbox form element using Bonsai_web_form and display its value, including error states, as a S-expression. ```ocaml let textbox_value (local_ graph) = let textbox = Form.Elements.Textbox.string ~allow_updates_when_focused:`Always () graph in let%arr textbox in let view = Form.view textbox in let value = Form.value textbox in Vdom.Node.div [ View.hbox ~gap:(`Px 5) [ Vdom.Node.text "my textbox"; view ] ; Vdom.Node.sexp_for_debugging ([%sexp_of: string Or_error.t] value) ] ;; ``` -------------------------------- ### Bonsai State and Stabilization with let%arr Source: https://github.com/janestreet/bonsai_web/blob/master/docs/public_garden_exports/thinking_in_bonsai.md This snippet demonstrates how to use `Bonsai.state'` to manage state and `let%arr` to create a reactive HTML output that updates when the state changes. The `let%arr` block runs during stabilization. ```ocaml let state, set_state = Bonsai.state' 0 graph in let%arr state and set_state in {%html|
%{state#Int}
|} ``` -------------------------------- ### OCaml: Rendering Components with Literal Module Path () using ppx_html Source: https://github.com/janestreet/bonsai_web/blob/master/docs/public_garden_exports/quick_start.md Demonstrates rendering a component using the `` syntax, which is suitable for literal module paths and does not support positional arguments. ```ocaml module Card = struct (* Component function with standard signature *) let view ?(attrs = []) children = {%html.jsx|
*{children}
|} ;; end let view = {%html.jsx|Hello, world!|} ``` -------------------------------- ### match%sub Called Within Bonsai Context Source: https://github.com/janestreet/bonsai_web/blob/master/docs/upgrade/local-graph.md When `match%sub` is called from within an existing Bonsai context, it is permissible. This example demonstrates its usage with `match%map`. ```ocaml let component () = match%map ... with | ... -> ... ``` -------------------------------- ### Trivial Widget Example Source: https://github.com/janestreet/bonsai_web/blob/master/docs/how_to/low_level_vdom.md Illustrates the basic API for creating a custom DOM widget. This widget displays an integer value that inverts its sign based on a boolean input. ```ocaml open Js_of_ocaml module Trivial_widget = struct (* Hooks force you to define the type of DOM node you're using. If you're not sure, or it's dynamic, you can use [Dom_html.element]. *) type dom = Dom_html.divElement let name = "trivial_widget" module Input = Bool module State = Int (* Just a helper function. *) let redraw input state element = let display = if input then state else state * -1 in element##.innerHTML := Js.string (sprintf "%d" display) ;; (* [create] will run on patch when the vdom is being converted to a DOM node. Note that there are NO SAFEGUARDS against script injection, bad inputs, etc. You are responsible for following security best practices. *) let create input = let state = Random.int 100 in let element = Dom_html.createDiv Dom_html.document in redraw input state element; state, element ;; (* [update] will run on every subsequent patch. *) let update ~prev_input ~input ~state ~element = match [%equal: Input.t] prev_input input with | true -> state, element | false -> redraw input state element; state, element ;; (* [destroy] will run when the widget is removed from the DOM. Do any cleanup here; e.g. removing event listeners, undoing side effects, etc. *) let destroy ~prev_input:_ ~state:_ ~element:_ = () (* As with hooks, widgets don't run in [Bonsai_web_test]. But widgets give us a bit more control over how their input is displayed in tests. You could also just pass [let to_vdom_for_testing = `Sexp_of_input]. *) let to_vdom_for_testing = `Custom (fun input -> Vdom.Node.create "trivial_widget" [ Vdom.Node.textf "Inverted: %b" input ]) ;; end (* As with [Vdom.Attr.Hooks.Make], it is important that [Vdom.Node.widget_of_module] is called exactly once for each "class" of widgets. *) let (trivial_widget : bool -> Vdom.Node.t) = unstage (Vdom.Node.widget_of_module (module Trivial_widget)) ;; ``` -------------------------------- ### Bonsai_extra.Mirror.mirror Type Signature Source: https://github.com/janestreet/bonsai_web/blob/master/docs/how_to/organizing_state.md The type signature for the mirror combinator, which synchronizes two Bonsai.t states. It requires functions for setting and getting values for both the store and interactive components. ```ocaml val mirror : ?sexp_of_model:('m -> Sexp.t) -> ?trigger:[ `Before_display | `After_display ] -> equal:('m -> 'm -> bool) -> store_set:('m -> unit Effect.t) Bonsai.t -> store_value:'m Bonsai.t -> interactive_set:('m -> unit Effect.t) Bonsai.t -> interactive_value:'m Bonsai.t -> local_ Bonsai.graph -> unit ``` -------------------------------- ### Run a Single Benchmark with Core_bench_js Source: https://github.com/janestreet/bonsai_web/blob/master/docs/how_to/benchmarking.md Execute a benchmark using Bonsai_bench.bench, which is a wrapper around Core_bench_js.bench. It allows configuring run parameters like quota. ```ocaml let () = let quota = Core_bench_js.Quota.Span (Time.Span.of_sec 1.0) in Bonsai_bench.bench ~run_config:(Core_bench_js.Run_config.create () ~quota) [ state ] ``` -------------------------------- ### Combine Effects with Effect.all_parallel_unit Source: https://github.com/janestreet/bonsai_web/blob/master/docs/public_garden_exports/quick_start.md Use `Effect.all_parallel_unit` to run multiple effects concurrently when they return unit. This example prevents default link navigation while updating a click counter and logging. ```ocaml module Custom_link = struct let component (graph @ local) = let clicked_count, set_count = Bonsai.state' 0 graph in let%arr clicked_count and set_count in let handle_click = Effect.all_parallel_unit [ (Effect.Prevent_default [@alert "-deprecated"]) ; set_count (fun count -> count + 1) ; Effect.print_s [%message "Link clicked!"] ] in {%html.jsx| |} ;; end ``` -------------------------------- ### Passing Arguments to Components Source: https://github.com/janestreet/bonsai_web/blob/master/docs/public_garden_exports/quick_start.md Illustrates how to pass named or optional arguments to components using the `~arg:%{value}` syntax. ```ocaml module Name_input = struct let view ?(placeholder_name = "") ~label () = {%html.jsx|
|} ;; end let placeholder_name = "Alice" let view = {%html.jsx||} ;; ``` -------------------------------- ### Example of Proc in use with complex composition Source: https://github.com/janestreet/bonsai_web/blob/master/docs/blog/history.md Illustrates a more complex usage of the 'proc' function to compose multiple arrow-like computations ('some_arrow', 'another_arrow', 'yet_another_arrow') with intermediate bindings and mappings. ```ocaml let my_arrow = proc (fun i -> let%bind x = some_arrow <<< (i >>| a_field) in let%bind y = another_arrow <<< let%map x = x and i = i in g x i in let%bind z = yet_another_arrow <<< let%map x = x and y = y and i = i in h x y i in z) ``` -------------------------------- ### Bonsai Component with Optional Configuration Argument Source: https://github.com/janestreet/bonsai_web/blob/master/docs/blog/proc.md Shows how to define a Bonsai component that accepts an optional configuration argument, allowing for flexible customization. ```ocaml module Config : sig type t = { should_show_header : bool; rows_to_display : int option; allow_tabs : bool } end val component : ?config:Config.t Value.t -> int list Value.t -> Vdom.Node.t Computation.t ``` -------------------------------- ### Getting Current Theme in Bonsai Source: https://github.com/janestreet/bonsai_web/blob/master/docs/how_to/theming.md Shows how to access the current theme within a Bonsai graph using View.Theme.current. This is useful for creating theme-aware components like error buttons. ```ocaml let error_button (local_ graph) = let theme = View.Theme.current graph in let%arr theme in View.button theme ~intent:Error ~on_click:do_thing "Error button" ;; ``` -------------------------------- ### Dynamic Theme Toggling with State Source: https://github.com/janestreet/bonsai_web/blob/master/docs/how_to/theming.md Illustrates dynamic theme switching by using Bonsai.state to manage the theme style (e.g., Dark/Light) and updating the theme accordingly. Includes a button to toggle the mode. ```ocaml let themed_theme_toggler ~toggle_dark (local_ graph) = let%arr theme = View.Theme.current graph and toggle_dark and error_button = error_button graph in View.hbox [ View.button theme ~on_click:toggle_dark "Toggle Dark Mode"; error_button ] ;; let app (local_ graph) = let theme_style, set_theme_style = Bonsai.state Kado.Style.Dark graph in let theme = let%arr theme_style in Kado.theme ~style:theme_style ~version:V1 () in let toggle_dark = let%arr theme_style and set_theme_style in set_theme_style (match theme_style with | Dark -> Light | Light -> Dark) in View.Theme.set_for_app theme (themed_theme_toggler ~toggle_dark) graph ;; ``` -------------------------------- ### Render Table Cells and Headers Source: https://github.com/janestreet/bonsai_web/blob/master/docs/how_to/partial_render_table.md Specifies how individual cells and table headers are rendered. This example uses `Stateful_rows` for cell rendering, providing access to row data and the local graph. ```ocaml module Table = Bonsai_web_partial_render_table.Basic let columns : (Symbol.t, Row.t, Col_id.t) Table.Columns.t = Table.Columns.build (module Col_id) ~columns:structure ~render_cell: ( Stateful_rows (fun _key data (local_ _graph) -> let%arr { Row.symbol; price; num_owned; last_updated } = data in fun col -> match col with | Col_id.Symbol -> Vdom.Node.text symbol | Price -> Vdom.Node.text (sprintf "%.2f" price) | Num_owned -> Vdom.Node.text (string_of_int num_owned) | Last_updated -> Vdom.Node.text (Time_ns.to_string last_updated))) ~render_header:(fun col (local_ _graph) -> match%arr col with | Symbol -> Vdom.Node.text "Symbol" | Price -> Vdom.Node.text "Price" | Num_owned -> Vdom.Node.text "Num_owned" | Last_updated -> Vdom.Node.text "Last Updated") ;; ``` -------------------------------- ### Setting Application Theme with Kado Source: https://github.com/janestreet/bonsai_web/blob/master/docs/how_to/theming.md Demonstrates how to set a theme for the entire application using View.Theme.set_for_app. It wraps the app's top-level component with a specific Kado theme. ```ocaml let app (local_ graph) = View.Theme.set_for_app (Kado.theme ~version:V1 () |> Bonsai.return) error_button graph ;; ``` -------------------------------- ### Configuring Single_page_handler for Url_var.t Source: https://github.com/janestreet/bonsai_web/blob/master/docs/how_to/url_var.md Example of configuring cohttp_static_handler's Single_page_handler to handle unknown URLs by defaulting to the index page, which is necessary for client-side navigation via Url_var.t to function correctly. ```ocaml Single_page_handler.create_handler ~title: ~assets:<assets> ~on_unknown_url:`Index ``` -------------------------------- ### Old Style Bonsai Component with Input and Result Modules Source: https://github.com/janestreet/bonsai_web/blob/master/docs/blog/proc.md Illustrates the traditional approach in Bonsai where component inputs and results were often defined using separate modules and record types. ```ocaml module Input : sig type t = { foo : int ; bar : string } end module Result : sig type t = { baz : float ; view : Vdom.Node.t } end val component : (Input.t, Result.t) Bonsai.t ``` -------------------------------- ### OCaml: Component with Children Syntax (<Component.view></>) with ppx_html Source: https://github.com/janestreet/bonsai_web/blob/master/docs/public_garden_exports/quick_start.md Demonstrates the syntax for components that accept children, where the function's last positional parameter must be `Vdom.Node.t list`. The `<Component.view></>` and `<Component.view></Component.view>` syntaxes are equivalent. ```ocaml module Component_with_children = struct (* Must end with Vdom.Node.t list *) let view (children : Vdom.Node.t list) = {%html.jsx|<div>*{children}</div>|} ;; end let view = {%html.jsx| <Component_with_children.view> <Component_with_children.view>Child 1</Component_with_children.view> <div>Child 2</div> </> |} ;; ``` -------------------------------- ### Using Connection Status in a UI Component Source: https://github.com/janestreet/bonsai_web/blob/master/docs/how_to/resetting_state.md Demonstrates how to use the connection status Bonsai.t as an input to a UI component that displays the connection state. It uses a match%sub to conditionally render different Vdom nodes based on the status. ```ocaml let conn = Connection.create ~uri:"https://google.com" let connection_status_ui (local_ graph) = let connection_status = match%sub connection_status graph conn with | `Connected -> Bonsai.return (Vdom.Node.div [ Vdom.Node.text "Connected" ]) | `Connecting -> Bonsai.return (Vdom.Node.div [ Vdom.Node.text "Connecting" ]) | `Disconnected -> Bonsai.return (Vdom.Node.div [ Vdom.Node.text "Disconnected" ]) in let%arr connection_status in Vdom.Node.div [ connection_status ] ;; ```