aboutsummaryrefslogtreecommitdiff
path: root/examples/99-full-stack-applications/client/src/app.gleam
diff options
context:
space:
mode:
authorHayleigh Thompson <me@hayleigh.dev>2024-06-16 10:01:31 +0100
committerHayleigh Thompson <me@hayleigh.dev>2024-06-16 10:01:31 +0100
commiteae7274bf965505b5144eb9362eb634fcd77019a (patch)
treed95f37cf2b6a18a0985c67ade89aaee23da5e602 /examples/99-full-stack-applications/client/src/app.gleam
parentf07188d0735808ccb313aedfd7f81889de253267 (diff)
downloadlustre-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.gleam127
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),
+ ]),
+ ])
+}