diff options
author | Hayleigh Thompson <me@hayleigh.dev> | 2024-06-16 10:01:31 +0100 |
---|---|---|
committer | Hayleigh Thompson <me@hayleigh.dev> | 2024-06-16 10:01:31 +0100 |
commit | eae7274bf965505b5144eb9362eb634fcd77019a (patch) | |
tree | d95f37cf2b6a18a0985c67ade89aaee23da5e602 /examples/99-full-stack-applications/client/src/app.gleam | |
parent | f07188d0735808ccb313aedfd7f81889de253267 (diff) | |
download | lustre-eae7274bf965505b5144eb9362eb634fcd77019a.tar.gz lustre-eae7274bf965505b5144eb9362eb634fcd77019a.zip |
:wrench: Update deps, pin gleam_json to 1.0.1
Diffstat (limited to 'examples/99-full-stack-applications/client/src/app.gleam')
-rw-r--r-- | examples/99-full-stack-applications/client/src/app.gleam | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/examples/99-full-stack-applications/client/src/app.gleam b/examples/99-full-stack-applications/client/src/app.gleam new file mode 100644 index 0000000..58715ca --- /dev/null +++ b/examples/99-full-stack-applications/client/src/app.gleam @@ -0,0 +1,127 @@ +// IMPORTS --------------------------------------------------------------------- + +import decipher +import gleam/dynamic.{dynamic} +import gleam/int +import gleam/list +import gleam/result +import lustre +import lustre/attribute +import lustre/effect.{type Effect} +import lustre/element.{type Element} +import lustre/element/html +import lustre/event + +// MAIN ------------------------------------------------------------------------ + +pub fn main() { + let app = lustre.application(init, update, view) + let assert Ok(_) = lustre.start(app, "#app", Nil) + + Nil +} + +// MOCEL ----------------------------------------------------------------------- + +type Model = + List(#(String, Int)) + +fn init(_) -> #(Model, Effect(Msg)) { + let model = [] + let effect = effect.none() + + #(model, effect) +} + +// UPDATE ---------------------------------------------------------------------- + +type Msg { + ServerSavedList(Result(Nil, String)) + UserAddedProduct(name: String) + UserSavedList + UserUpdatedQuantity(name: String, amount: Int) +} + +fn update(model: Model, msg: Msg) -> #(Model, Effect(Msg)) { + case msg { + ServerSavedList(_) -> #(model, effect.none()) + UserAddedProduct(name) -> #([#(name, 1), ..model], effect.none()) + UserSavedList -> #(model, effect.none()) + UserUpdatedQuantity(name, quantity) -> { + let model = + list.map(model, fn(item) { + case item.0 == name { + True -> #(name, quantity) + False -> item + } + }) + + #(model, effect.none()) + } + } +} + +// VIEW ------------------------------------------------------------------------ + +fn view(model: Model) -> Element(Msg) { + let styles = [ + #("max-width", "30ch"), + #("margin", "0 auto"), + #("display", "flex"), + #("flex-direction", "column"), + #("gap", "1em"), + ] + + html.div([attribute.style(styles)], [ + view_grocery_list(model), + view_new_item(), + html.div([], [html.button([], [html.text("Sync")])]), + ]) +} + +fn view_new_item() -> Element(Msg) { + let handle_click = fn(event) { + let path = ["target", "previousElementSibling", "value"] + + event + |> decipher.at(path, dynamic.string) + |> result.map(UserAddedProduct) + } + + html.div([], [ + html.input([]), + html.button([event.on("click", handle_click)], [html.text("Add")]), + ]) +} + +fn view_grocery_list(model: Model) -> Element(Msg) { + let styles = [#("display", "flex"), #("flex-direction", "column-reverse")] + + element.keyed(html.div([attribute.style(styles)], _), { + use #(name, quantity) <- list.map(model) + let item = view_grocery_item(name, quantity) + + #(name, item) + }) +} + +fn view_grocery_item(name: String, quantity: Int) -> Element(Msg) { + let handle_input = fn(e) { + event.value(e) + |> result.nil_error + |> result.then(int.parse) + |> result.map(UserUpdatedQuantity(name, _)) + |> result.replace_error([]) + } + + html.div([attribute.style([#("display", "flex"), #("gap", "1em")])], [ + html.span([attribute.style([#("flex", "1")])], [html.text(name)]), + html.input([ + attribute.style([#("width", "4em")]), + attribute.type_("number"), + attribute.value(int.to_string(quantity)), + attribute.min("0"), + event.on("input", handle_input), + ]), + ]) +} |