From bbefb97ba3d8f117b8cbef769180a4d8425cf145 Mon Sep 17 00:00:00 2001 From: Eileen Noonan Date: Mon, 1 Apr 2024 20:08:22 -0400 Subject: =?UTF-8?q?=E2=9C=A8=20Create=20a=20routing=20example=20using=20mo?= =?UTF-8?q?dem.=20(#96)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/07-routing/README.md | 6 +- examples/07-routing/gleam.toml | 2 +- examples/07-routing/header.png | Bin 0 -> 93127 bytes examples/07-routing/manifest.toml | 14 ++-- examples/07-routing/src/app.gleam | 134 +++++++++++++++++++++++++++++++++++--- 5 files changed, 137 insertions(+), 19 deletions(-) create mode 100644 examples/07-routing/header.png diff --git a/examples/07-routing/README.md b/examples/07-routing/README.md index 6c0b261..5eaf0cd 100644 --- a/examples/07-routing/README.md +++ b/examples/07-routing/README.md @@ -2,12 +2,14 @@ # 07 Routing +We haven't quite got round to documenting this example yet. You can still check out the [source code](./src/app.gleam) and run it with the following command: + ```sh -gleam run -m lustre/dev start --spa +gleam run -m lustre/dev start --use-example-styles ``` ## Getting help -If you're having trouble with Lustre or not sure what the right way to do +If you're having trouble with Lustre or are not sure what the right way to do something is, the best place to get help is the [Gleam Discord server](https://discord.gg/Fm8Pwmy). You could also open an issue on the [Lustre GitHub repository](https://github.com/lustre-labs/lustre/issues). diff --git a/examples/07-routing/gleam.toml b/examples/07-routing/gleam.toml index 5930290..cdecf3c 100644 --- a/examples/07-routing/gleam.toml +++ b/examples/07-routing/gleam.toml @@ -11,4 +11,4 @@ modem = "~> 1.0" [dev-dependencies] gleeunit = "~> 1.0" -lustre_dev_tools = "~> 1.0" +lustre_dev_tools = "~> 1.1" diff --git a/examples/07-routing/header.png b/examples/07-routing/header.png new file mode 100644 index 0000000..ffee3c6 Binary files /dev/null and b/examples/07-routing/header.png differ diff --git a/examples/07-routing/manifest.toml b/examples/07-routing/manifest.toml index 954f1a3..6000b9b 100644 --- a/examples/07-routing/manifest.toml +++ b/examples/07-routing/manifest.toml @@ -15,15 +15,15 @@ packages = [ { name = "gleam_package_interface", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_package_interface", source = "hex", outer_checksum = "52A721BCA972C8099BB881195D821AAA64B9F2655BECC102165D5A1097731F01" }, { name = "gleam_stdlib", version = "0.36.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "C0D14D807FEC6F8A08A7C9EF8DFDE6AE5C10E40E21325B2B29365965D82EB3D4" }, { name = "glearray", version = "0.2.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "glearray", source = "hex", outer_checksum = "908154F695D330E06A37FAB2C04119E8F315D643206F8F32B6A6C14A8709FFF4" }, - { name = "gleeunit", version = "1.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "D364C87AFEB26BDB4FB8A5ABDE67D635DC9FA52D6AB68416044C35B096C6882D" }, + { name = "gleeunit", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "72CDC3D3F719478F26C4E2C5FED3E657AC81EC14A47D2D2DEBB8693CA3220C3B" }, { name = "glint", version = "0.18.0", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_community_colour", "gleam_stdlib", "snag"], otp_app = "glint", source = "hex", outer_checksum = "BB0F14643CC51C069A5DC6E9082EAFCD9967AFD1C9CC408803D1A40A3FD43B54" }, - { name = "lustre", version = "4.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib"], otp_app = "lustre", source = "hex", outer_checksum = "1D40C1378279F7015687F8C9DB739D6880BB0B843F4428B85C61EDDA8BF21FC6" }, - { name = "lustre_dev_tools", version = "1.0.0", build_tools = ["gleam"], requirements = ["argv", "filepath", "gleam_community_ansi", "gleam_erlang", "gleam_json", "gleam_otp", "gleam_package_interface", "gleam_stdlib", "glint", "simplifile", "spinner", "tom"], otp_app = "lustre_dev_tools", source = "hex", outer_checksum = "66142ADDCA3D6C63A89E016CF6C21E07D06D6DC92479325182A07C360BD026D3" }, + { name = "lustre", version = "4.1.4", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib"], otp_app = "lustre", source = "hex", outer_checksum = "BA9ED993187B0BB721FFBB1F01F6CCA14548F68873C55567B77918D33D0D9ECB" }, + { name = "lustre_dev_tools", version = "1.1.1", build_tools = ["gleam"], requirements = ["argv", "filepath", "gleam_community_ansi", "gleam_erlang", "gleam_json", "gleam_otp", "gleam_package_interface", "gleam_stdlib", "glint", "simplifile", "spinner", "tom"], otp_app = "lustre_dev_tools", source = "hex", outer_checksum = "16E34D9441FFCA0F5A4E13EAA910228275867F88971EC79ECD3F802375EA82BE" }, { name = "lustre_http", version = "0.5.2", build_tools = ["gleam"], requirements = ["gleam_fetch", "gleam_http", "gleam_javascript", "gleam_json", "gleam_stdlib", "lustre"], otp_app = "lustre_http", source = "hex", outer_checksum = "FB0478CBFA6B16DBE8ECA326DAE2EC15645E04900595EF2C4F039ABFA0512ABA" }, - { name = "lustre_ui", version = "0.5.0", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_json", "gleam_stdlib", "lustre"], otp_app = "lustre_ui", source = "hex", outer_checksum = "7ECB5414BE926082401891C62FAAA21221FC0B7A2F0568A492349F48DC2B02A0" }, + { name = "lustre_ui", version = "0.6.0", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_json", "gleam_stdlib", "lustre"], otp_app = "lustre_ui", source = "hex", outer_checksum = "FA1F9E89D89CDD5DF376ED86ABA8A38441CB2E664CD4D402F22A49DA4D7BB56D" }, { name = "modem", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "lustre"], otp_app = "modem", source = "hex", outer_checksum = "584897B281E86458C513D9FB0D00C143507FA684042616A87BD4A4695B272FDE" }, { name = "repeatedly", version = "2.1.1", build_tools = ["gleam"], requirements = [], otp_app = "repeatedly", source = "hex", outer_checksum = "38808C3EC382B0CD981336D5879C24ECB37FCB9C1D1BD128F7A80B0F74404D79" }, - { name = "simplifile", version = "1.5.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "C44DB387524F90DC42142699C78C850003289D32C7C99C7D32873792A299CDF7" }, + { name = "simplifile", version = "1.6.1", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "B75D3C64E526D9D7EDEED5F3BA31DAAF5F2B4D80A4183FE17FDB02ED526E4E96" }, { name = "snag", version = "0.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "snag", source = "hex", outer_checksum = "54D32E16E33655346AA3E66CBA7E191DE0A8793D2C05284E3EFB90AD2CE92BCC" }, { name = "spinner", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_erlang", "gleam_stdlib", "glearray", "repeatedly"], otp_app = "spinner", source = "hex", outer_checksum = "200BA3D4A04D468898E63C0D316E23F526E02514BC46454091975CB5BAE41E8F" }, { name = "thoas", version = "0.4.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "4918D50026C073C4AB1388437132C77A6F6F7C8AC43C60C13758CC0ADCE2134E" }, @@ -34,7 +34,7 @@ packages = [ gleam_stdlib = { version = "~> 0.36" } gleeunit = { version = "~> 1.0" } lustre = { version = "~> 4.0" } -lustre_dev_tools = { version = "~> 1.0" } +lustre_dev_tools = { version = "~> 1.1" } lustre_http = { version = "~> 0.5.2" } lustre_ui = { version = "~> 0.4" } -modem = { version = "~> 1.0"} +modem = { version = "~> 1.0" } diff --git a/examples/07-routing/src/app.gleam b/examples/07-routing/src/app.gleam index a9d14d0..382bd60 100644 --- a/examples/07-routing/src/app.gleam +++ b/examples/07-routing/src/app.gleam @@ -1,4 +1,8 @@ import gleam/uri.{type Uri} +import gleam/list +import gleam/string +import gleam/result +import gleam/dynamic import lustre import lustre/attribute import lustre/effect.{type Effect} @@ -15,7 +19,6 @@ import modem // In your own apps, make sure to add the `lustre/ui` dependency and include the // stylesheet somewhere. import lustre/ui -import lustre/ui/aside // MAIN ------------------------------------------------------------------------ @@ -27,31 +30,144 @@ pub fn main() { // MODEL ----------------------------------------------------------------------- type Model { - Model + Model(current: Route, guests: List(Guest), new_guest_name: String) +} + +type Route { + Home + WelcomeGuest(value: String) +} + +type Guest { + Guest(slug: String, name: String) } fn init(_) -> #(Model, Effect(Msg)) { - #(Model, modem.init(on_route_change)) + #( + Model( + current: Home, + guests: [ + Guest(slug: "chihiro", name: "Chihiro"), + Guest(slug: "totoro", name: "Totoro"), + ], + new_guest_name: "", + ), + modem.init(on_route_change), + ) } fn on_route_change(uri: Uri) -> Msg { - let path = uri.path_segments(uri.path) - - RouterPushedPath(path) + case uri.path_segments(uri.path) { + ["welcome", guest] -> OnRouteChange(WelcomeGuest(guest)) + _ -> OnRouteChange(Home) + } } // UPDATE ---------------------------------------------------------------------- pub opaque type Msg { - RouterPushedPath(List(String)) + OnRouteChange(Route) + UserUpdatedNewGuestName(String) + UserAddedNewGuest(Guest) } fn update(model: Model, msg: Msg) -> #(Model, Effect(Msg)) { - todo + case msg { + OnRouteChange(route) -> #(Model(..model, current: route), effect.none()) + UserUpdatedNewGuestName(name) -> #( + Model(..model, new_guest_name: name), + effect.none(), + ) + UserAddedNewGuest(guest) -> #( + Model( + ..model, + guests: list.append(model.guests, [guest]), + new_guest_name: "", + ), + effect.none(), + ) + } } // VIEW ------------------------------------------------------------------------ fn view(model: Model) -> Element(Msg) { - todo + let styles = [#("margin", "15vh")] + + let page = case model.current { + WelcomeGuest(name) -> render_welcome(model, name) + Home -> render_home(model) + } + + ui.stack([attribute.style(styles)], [render_nav(model), page]) +} + +fn render_nav(model: Model) -> Element(a) { + let item_styles = [#("margin", "15px"), #("text-decoration", "underline")] + + let nav_item = fn(path, text) { + html.a([attribute.href("/" <> path), attribute.style(item_styles)], [ + element.text(text), + ]) + } + + let guests = + model.guests + |> list.map(fn(guest: Guest) { + nav_item("welcome/" <> guest.slug, guest.name) + }) + + html.nav([], [nav_item("", "Home"), ..guests]) +} + +fn render_home(model: Model) { + let new_guest_input = fn(event) { + use code <- result.try(dynamic.field("key", dynamic.string)(event)) + case code { + "Enter" -> { + let guest_slug = + model.new_guest_name + |> string.replace(" ", "-") + |> string.lowercase + Ok( + UserAddedNewGuest(Guest(name: model.new_guest_name, slug: guest_slug)), + ) + } + _ -> { + use value <- result.try(event.value(event)) + Ok(UserUpdatedNewGuestName(value)) + } + } + } + ui.centre( + [attribute.style([#("margin-top", "10vh")])], + ui.stack([], [ + to_title("Welcome to the Party 🏡"), + html.label([], [element.text("Please sign the guest book:")]), + ui.input([ + event.on("keyup", new_guest_input), + attribute.value(model.new_guest_name), + ]), + ]), + ) +} + +fn render_welcome(model: Model, slug) -> Element(a) { + let guest = + model.guests + |> list.find(fn(guest: Guest) { guest.slug == slug }) + + let title = case guest { + Ok(guest) -> to_title("Hello, " <> guest.name <> "! 🎉") + _ -> to_title("Sorry ... didn't quite catch that.") + } + + ui.centre([attribute.style([#("margin-top", "10vh")])], title) +} + +fn to_title(text) { + html.h1( + [attribute.style([#("font-size", "36px"), #("font-weight", "bold")])], + [element.text(text)], + ) } -- cgit v1.2.3