diff options
43 files changed, 616 insertions, 1018 deletions
diff --git a/examples/01-hello-world/README.md b/examples/01-hello-world/README.md new file mode 100644 index 0000000..5302fe3 --- /dev/null +++ b/examples/01-hello-world/README.md @@ -0,0 +1,102 @@ +# 01 Hello World + +This hello world example is a tiny example of what you need to put together to +get a Lustre application running. In later examples we'll touch on server-side +rendering and Lustre Universal Components but for these first examples we'll +be looking at rendering on the client _only_. + +## Configuring the Gleam project + +It's important to remember to add `target = "javascript"` to your `gleam.toml`! +If you forget to do this you might end up confused when it looks like your project +is successfully building but you have no JavaScript output! + +## Creating a `lustre.element` application + +The simplest kind of Lustre application is the `element`. This sets up a static +application that does not have its own update loop and cannot dynamically render +any content. Instead, we provide a static Lustre `Element` to render once. + +### HTML attributes and inline styles + +In Lustre, HTML attributes are modelled as a `List` of attributes. This is a bit +different from many other frameworks that use an object or record for attributes. +Lustre takes the list-of-attributes approach for a couple of reasons: + +- Gleam doesn't have a way to construct an anonymous record: we'd have to have + an infinite number of types to cover every possible varation! + +- Working with lists makes it convenient to merge different sets of attributes + together (like an element that defines some local attributes and merges them + with any passed in as an argument). + +In a similar fashion, inline styles are lists of property/value tuples. In this +example we're setting inline styles for the `width` and `height` properties. + +### Why `element.text`? + +In frameworks like React, it's enough to just return a `String` if you want to +render some text. Gleam's type system works a little differently though, a string +literal isn't compatible with Lustre's `Element` type on its own, so we need to +wrap any text to render in `element.text`. + +You won't see us do it in any of the examples we share, but it's common for folks +to import `text` and any html elements they're using unqualified to cut down on +some of the noise: + +```gleam +import lustre/element.{text} +import lustre/element/html.{div, p} +... +``` + +## Starting a Lustre application + +Starting a Lustre application with `lustre.start` requires three things: + +- A configured `Application` (that's what we used `lustre.element` for). + +- A [CSS selector](https://developer.mozilla.org/en-US/docs/Web/API/Document_object_model/Locating_DOM_elements_using_selectors) + to locate the DOM node to mount the application on to. As in other frameworks, + it's common to use an element with the id "app": for that you'd write the + selector as `#app`. + +- Some initial data to pass to the application's `init` function. Because applications + constructed with `lustre.element` are not dynamic there's nothing meaningful + to pass in here, so we just use `Nil`. + +Starting an application could fail for a number of reasons, so this function +returns a `Result`. The `Ok` value is a function you can use to send messages to +your running application from the outside world: we'll see more of that in later +examples! + +## Seeing the result + +Lustre ships with a very simple development server to help you look through these +examples. You can run `gleam run -m lustre/try` in any of these examples to start +this development server and head over to `localhost:1234` to see what it produces. + +If you're coming from a more mature Web development setup, you should know that +this preview server is _not_ a replacement for a more robust development setup! +While we work on building this into Lustre we recommend using [vite](https://vitejs.dev) +with the [vite-gleam](https://www.npmjs.com/package/vite-gleam) plugin. + +### Enabling lustre_ui + +[Lustre_ui](https://hexdocs.pm/lustre_ui/) is a separate package published by us +to provide a collection of robust styled elements for folks that want to get working +with Lustre ASAP. Each of these examples have been written to use elements from +that package. + +The lustre/try preview server can be configured to include the lustre_ui stylesheet +by passing the `--include-styles` flag: + +```sh +$ gleam run -m lustre/try -- --include-styles +``` + +Note that the first `--` is necessary so the Gleam binary knows this is a flag +that should be passed to lustre/try! + +It's not necessary to use lustre_ui to use Lustre or to check out any of these +examples, but the option is there if you want it. diff --git a/examples/01-hello-world/gleam.toml b/examples/01-hello-world/gleam.toml new file mode 100644 index 0000000..4368c0c --- /dev/null +++ b/examples/01-hello-world/gleam.toml @@ -0,0 +1,11 @@ +name = "app" +version = "1.0.0" +target = "javascript" + +[dependencies] +gleam_stdlib = "~> 0.34 or ~> 1.0" +lustre = { path = "../../" } +lustre_ui = "~> 0.3" + +[dev-dependencies] +gleeunit = "~> 1.0" diff --git a/examples/01-hello-world/manifest.toml b/examples/01-hello-world/manifest.toml new file mode 100644 index 0000000..4033048 --- /dev/null +++ b/examples/01-hello-world/manifest.toml @@ -0,0 +1,24 @@ +# This file was generated by Gleam +# You typically do not need to edit this file + +packages = [ + { name = "argv", version = "1.0.1", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "A6E9009E50BBE863EB37D963E4315398D41A3D87D0075480FC244125808F964A" }, + { name = "gleam_community_ansi", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "gleam_community_colour"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "FE79E08BF97009729259B6357EC058315B6FBB916FAD1C2FF9355115FEB0D3A4" }, + { name = "gleam_community_colour", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "A49A5E3AE8B637A5ACBA80ECB9B1AFE89FD3D5351FF6410A42B84F666D40D7D5" }, + { name = "gleam_erlang", version = "0.24.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "26BDB52E61889F56A291CB34167315780EE4AA20961917314446542C90D1C1A0" }, + { name = "gleam_json", version = "0.7.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "CB405BD93A8828BCD870463DE29375E7B2D252D9D124C109E5B618AAC00B86FC" }, + { name = "gleam_otp", version = "0.9.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "5FADBBEC5ECF3F8B6BE91101D432758503192AE2ADBAD5602158977341489F71" }, + { name = "gleam_stdlib", version = "0.34.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "1FB8454D2991E9B4C0C804544D8A9AD0F6184725E20D63C3155F0AEB4230B016" }, + { name = "gleeunit", version = "1.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "D364C87AFEB26BDB4FB8A5ABDE67D635DC9FA52D6AB68416044C35B096C6882D" }, + { name = "glint", version = "0.14.0", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_stdlib", "snag", "gleam_community_colour"], otp_app = "glint", source = "hex", outer_checksum = "21AB16D5A50D4EF34DF935915FDBEE06B2DAEDEE3FCC8584C6E635A866566B38" }, + { name = "lustre", version = "3.1.3", build_tools = ["gleam"], requirements = ["argv", "gleam_community_ansi", "gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib", "glint"], source = "local", path = "../.." }, + { name = "lustre_ui", version = "0.3.0", build_tools = ["gleam"], requirements = ["gleam_community_colour", "lustre", "gleam_stdlib"], otp_app = "lustre_ui", source = "hex", outer_checksum = "F81BE5D20153CFFC717C2C4687A19375A8613528908AF7069DDA1B929C8398B1" }, + { name = "snag", version = "0.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "snag", source = "hex", outer_checksum = "54D32E16E33655346AA3E66CBA7E191DE0A8793D2C05284E3EFB90AD2CE92BCC" }, + { name = "thoas", version = "0.4.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "4918D50026C073C4AB1388437132C77A6F6F7C8AC43C60C13758CC0ADCE2134E" }, +] + +[requirements] +gleam_stdlib = { version = "~> 0.34 or ~> 1.0" } +gleeunit = { version = "~> 1.0" } +lustre = { path = "../../" } +lustre_ui = { version = "~> 0.3"} diff --git a/examples/01-hello-world/src/app.gleam b/examples/01-hello-world/src/app.gleam new file mode 100644 index 0000000..dc42ad7 --- /dev/null +++ b/examples/01-hello-world/src/app.gleam @@ -0,0 +1,27 @@ +import lustre +import lustre/attribute +import lustre/element +import lustre/element/html +// These examples are written with lustre_ui in mind. They'll work regardless, +// but to see what lustre_ui can do make sure to run each of these examples with +// the `--include-styles` flag: +// +// $ gleam run -m lustre/try -- --include-styles +// +// In your own apps, make sure to add the `lustre_ui` dependency and include the +// stylesheet somewhere. +import lustre/ui + +pub fn main() { + let styles = [#("width", "100vw"), #("height", "100vh")] + let app = + lustre.element(ui.centre( + [attribute.style(styles)], + html.div([], [ + html.h1([], [element.text("Hello, world.")]), + html.h2([], [element.text("Welcome to Lustre.")]), + ]), + )) + + let assert Ok(_) = lustre.start(app, "#app", Nil) +} diff --git a/examples/02-interactivity/README.md b/examples/02-interactivity/README.md new file mode 100644 index 0000000..d97f618 --- /dev/null +++ b/examples/02-interactivity/README.md @@ -0,0 +1,111 @@ +# 02 Interactivity + +In this example we show the basic structure of all Lustre applications with a +classic counter example. + +## The Model-View-Update architecture + +All Lustre applications are built around the Model-View-Update (MVU) architecture. +This is a pattern that's been popularised by the Elm programming language and +has since been adopted by many other frameworks and languages. + +MVU applications are built around three main concepts: + +- A `Model` and a function to initialise it. +- A `Msg` type and a function to update the model based on messages. +- A `View` function to render the model as a Lustre `Element`. + +These three pieces come together to form a self-contained update loop. You produce +an initial model, render it as HTML, and convert any user interactions into +messages to handle in the update function. + + ```text + +--------+ + | | + | update | + | | + +--------+ + ^ | + | | + Msg | | Model + | | + | v ++------+ +------------------------+ +| | Model | | +| init |------------------------>| Lustre Runtime | +| | | | ++------+ +------------------------+ + ^ | + | | + Msg | | Model + | | + | v + +--------+ + | | + | view | + | | + +--------+ +``` + +### Model + +The model represents the entire state of your application. For most Lustre +applications this will be a record, but for this example we're aliasing `Int` to +our `Model` type to keep things simple. + +We also need to write an `init` function that returns the initial state of our +application. It takes one argument, known as "flags" which is provided when the +application is first started. + +```gleam +fn init(initial_count: Int) -> Model { + case initial_count < 0 { + True -> 0 + False -> initial_count + } +} +``` + +Our `init` function takes a starting count, but ensures it cannot be below `0`. + +### Update + +In many other frameworks, it's common to update state directly in an event handler. +MVU applications take a different approach: instead of state updates being scattered +around your codebase, they are handled in a single `update` function. + +To achieve this, we define a `Msg` type that represents all the different kinds of +messages our application can receive. If you're familiar with Erlang this approach +to state management will be familiar to you. If you're coming from a JavaScript +background, this approach is most-similar to state management solutions like Redux +or Vuex. + +```gleam +pub opaque type Msg { + Incr + Decr +} +``` + +This approach means it is easy to quickly get an idea of all the ways your app +can change state, and makes it easy to add new state changes over time. By pattern +matching on an incoming message in our `update` function, we can lean on Gleam's +_exhaustiveness checking_ to ensure we handle all possible messages. + +### View + + + +```gleam +fn view(model: Model) -> Element(Msg) { + ... +} +``` + + +## Creating a dynamic Lustre application + +In the previous example we used the `lustre.element` function to construct a +static Lustre app. To construct a simple interactive app we can use `lustre.simple` +instead. From now on we'll see that all the different ways to construct a Lustre +application all take the same three `init`, `update`, and `view` functions. diff --git a/examples/02-interactivity/gleam.toml b/examples/02-interactivity/gleam.toml new file mode 100644 index 0000000..4368c0c --- /dev/null +++ b/examples/02-interactivity/gleam.toml @@ -0,0 +1,11 @@ +name = "app" +version = "1.0.0" +target = "javascript" + +[dependencies] +gleam_stdlib = "~> 0.34 or ~> 1.0" +lustre = { path = "../../" } +lustre_ui = "~> 0.3" + +[dev-dependencies] +gleeunit = "~> 1.0" diff --git a/examples/02-interactivity/manifest.toml b/examples/02-interactivity/manifest.toml new file mode 100644 index 0000000..4033048 --- /dev/null +++ b/examples/02-interactivity/manifest.toml @@ -0,0 +1,24 @@ +# This file was generated by Gleam +# You typically do not need to edit this file + +packages = [ + { name = "argv", version = "1.0.1", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "A6E9009E50BBE863EB37D963E4315398D41A3D87D0075480FC244125808F964A" }, + { name = "gleam_community_ansi", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "gleam_community_colour"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "FE79E08BF97009729259B6357EC058315B6FBB916FAD1C2FF9355115FEB0D3A4" }, + { name = "gleam_community_colour", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "A49A5E3AE8B637A5ACBA80ECB9B1AFE89FD3D5351FF6410A42B84F666D40D7D5" }, + { name = "gleam_erlang", version = "0.24.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "26BDB52E61889F56A291CB34167315780EE4AA20961917314446542C90D1C1A0" }, + { name = "gleam_json", version = "0.7.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "CB405BD93A8828BCD870463DE29375E7B2D252D9D124C109E5B618AAC00B86FC" }, + { name = "gleam_otp", version = "0.9.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "5FADBBEC5ECF3F8B6BE91101D432758503192AE2ADBAD5602158977341489F71" }, + { name = "gleam_stdlib", version = "0.34.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "1FB8454D2991E9B4C0C804544D8A9AD0F6184725E20D63C3155F0AEB4230B016" }, + { name = "gleeunit", version = "1.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "D364C87AFEB26BDB4FB8A5ABDE67D635DC9FA52D6AB68416044C35B096C6882D" }, + { name = "glint", version = "0.14.0", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_stdlib", "snag", "gleam_community_colour"], otp_app = "glint", source = "hex", outer_checksum = "21AB16D5A50D4EF34DF935915FDBEE06B2DAEDEE3FCC8584C6E635A866566B38" }, + { name = "lustre", version = "3.1.3", build_tools = ["gleam"], requirements = ["argv", "gleam_community_ansi", "gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib", "glint"], source = "local", path = "../.." }, + { name = "lustre_ui", version = "0.3.0", build_tools = ["gleam"], requirements = ["gleam_community_colour", "lustre", "gleam_stdlib"], otp_app = "lustre_ui", source = "hex", outer_checksum = "F81BE5D20153CFFC717C2C4687A19375A8613528908AF7069DDA1B929C8398B1" }, + { name = "snag", version = "0.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "snag", source = "hex", outer_checksum = "54D32E16E33655346AA3E66CBA7E191DE0A8793D2C05284E3EFB90AD2CE92BCC" }, + { name = "thoas", version = "0.4.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "4918D50026C073C4AB1388437132C77A6F6F7C8AC43C60C13758CC0ADCE2134E" }, +] + +[requirements] +gleam_stdlib = { version = "~> 0.34 or ~> 1.0" } +gleeunit = { version = "~> 1.0" } +lustre = { path = "../../" } +lustre_ui = { version = "~> 0.3"} diff --git a/examples/02-interactivity/src/app.gleam b/examples/02-interactivity/src/app.gleam new file mode 100644 index 0000000..e47b7a4 --- /dev/null +++ b/examples/02-interactivity/src/app.gleam @@ -0,0 +1,66 @@ +import gleam/int +import lustre +import lustre/attribute +import lustre/element.{type Element} +import lustre/element/html +import lustre/event +// These examples are written with lustre_ui in mind. They'll work regardless, +// but to see what lustre_ui can do make sure to run each of these examples with +// the `--include-styles` flag: +// +// $ gleam run -m lustre/try -- --include-styles +// +// In your own apps, make sure to add the `lustre_ui` dependency and include the +// stylesheet somewhere. +import lustre/ui + +// MAIN ------------------------------------------------------------------------ + +pub fn main() { + let app = lustre.simple(init, update, view) + let assert Ok(_) = lustre.start(app, "#app", 0) +} + +// MODEL ----------------------------------------------------------------------- + +type Model = + Int + +fn init(initial_count: Int) -> Model { + case initial_count < 0 { + True -> 0 + False -> initial_count + } +} + +// UPDATE ---------------------------------------------------------------------- + +pub opaque type Msg { + Incr + Decr +} + +fn update(model: Model, msg: Msg) -> Model { + case msg { + Incr -> model + 1 + Decr -> model - 1 + } +} + +// VIEW ------------------------------------------------------------------------ + +fn view(model: Model) -> Element(Msg) { + let count = int.to_string(model) + let styles = [#("width", "100vw"), #("height", "100vh")] + + ui.centre( + [attribute.style(styles)], + ui.stack([], [ + ui.button([event.on_click(Incr)], [element.text("+")]), + html.p([attribute.style([#("text-align", "center")])], [ + element.text(count), + ]), + ui.button([event.on_click(Decr)], [element.text("-")]), + ]), + ) +} diff --git a/examples/03-controlled-inputs/README.md b/examples/03-controlled-inputs/README.md new file mode 100644 index 0000000..5326f25 --- /dev/null +++ b/examples/03-controlled-inputs/README.md @@ -0,0 +1 @@ +# 03 Controlled Inputs diff --git a/examples/03-controlled-inputs/gleam.toml b/examples/03-controlled-inputs/gleam.toml new file mode 100644 index 0000000..4368c0c --- /dev/null +++ b/examples/03-controlled-inputs/gleam.toml @@ -0,0 +1,11 @@ +name = "app" +version = "1.0.0" +target = "javascript" + +[dependencies] +gleam_stdlib = "~> 0.34 or ~> 1.0" +lustre = { path = "../../" } +lustre_ui = "~> 0.3" + +[dev-dependencies] +gleeunit = "~> 1.0" diff --git a/examples/03-controlled-inputs/manifest.toml b/examples/03-controlled-inputs/manifest.toml new file mode 100644 index 0000000..4033048 --- /dev/null +++ b/examples/03-controlled-inputs/manifest.toml @@ -0,0 +1,24 @@ +# This file was generated by Gleam +# You typically do not need to edit this file + +packages = [ + { name = "argv", version = "1.0.1", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "A6E9009E50BBE863EB37D963E4315398D41A3D87D0075480FC244125808F964A" }, + { name = "gleam_community_ansi", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "gleam_community_colour"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "FE79E08BF97009729259B6357EC058315B6FBB916FAD1C2FF9355115FEB0D3A4" }, + { name = "gleam_community_colour", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "A49A5E3AE8B637A5ACBA80ECB9B1AFE89FD3D5351FF6410A42B84F666D40D7D5" }, + { name = "gleam_erlang", version = "0.24.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "26BDB52E61889F56A291CB34167315780EE4AA20961917314446542C90D1C1A0" }, + { name = "gleam_json", version = "0.7.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "CB405BD93A8828BCD870463DE29375E7B2D252D9D124C109E5B618AAC00B86FC" }, + { name = "gleam_otp", version = "0.9.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "5FADBBEC5ECF3F8B6BE91101D432758503192AE2ADBAD5602158977341489F71" }, + { name = "gleam_stdlib", version = "0.34.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "1FB8454D2991E9B4C0C804544D8A9AD0F6184725E20D63C3155F0AEB4230B016" }, + { name = "gleeunit", version = "1.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "D364C87AFEB26BDB4FB8A5ABDE67D635DC9FA52D6AB68416044C35B096C6882D" }, + { name = "glint", version = "0.14.0", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_stdlib", "snag", "gleam_community_colour"], otp_app = "glint", source = "hex", outer_checksum = "21AB16D5A50D4EF34DF935915FDBEE06B2DAEDEE3FCC8584C6E635A866566B38" }, + { name = "lustre", version = "3.1.3", build_tools = ["gleam"], requirements = ["argv", "gleam_community_ansi", "gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib", "glint"], source = "local", path = "../.." }, + { name = "lustre_ui", version = "0.3.0", build_tools = ["gleam"], requirements = ["gleam_community_colour", "lustre", "gleam_stdlib"], otp_app = "lustre_ui", source = "hex", outer_checksum = "F81BE5D20153CFFC717C2C4687A19375A8613528908AF7069DDA1B929C8398B1" }, + { name = "snag", version = "0.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "snag", source = "hex", outer_checksum = "54D32E16E33655346AA3E66CBA7E191DE0A8793D2C05284E3EFB90AD2CE92BCC" }, + { name = "thoas", version = "0.4.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "4918D50026C073C4AB1388437132C77A6F6F7C8AC43C60C13758CC0ADCE2134E" }, +] + +[requirements] +gleam_stdlib = { version = "~> 0.34 or ~> 1.0" } +gleeunit = { version = "~> 1.0" } +lustre = { path = "../../" } +lustre_ui = { version = "~> 0.3"} diff --git a/examples/03-controlled-inputs/src/app.gleam b/examples/03-controlled-inputs/src/app.gleam new file mode 100644 index 0000000..8e713ea --- /dev/null +++ b/examples/03-controlled-inputs/src/app.gleam @@ -0,0 +1,74 @@ +import gleam/int +import gleam/string +import lustre +import lustre/attribute +import lustre/element.{type Element} +import lustre/event +// These examples are written with lustre_ui in mind. They'll work regardless, +// but to see what lustre_ui can do make sure to run each of these examples with +// the `--include-styles` flag: +// +// $ gleam run -m lustre/try -- --include-styles +// +// 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 ------------------------------------------------------------------------ + +pub fn main() { + let app = lustre.simple(init, update, view) + let assert Ok(_) = lustre.start(app, "#app", Nil) +} + +// MODEL ----------------------------------------------------------------------- + +type Model { + Model(value: String, length: Int, max: Int) +} + +fn init(_) -> Model { + Model(value: "", length: 0, max: 10) +} + +// UPDATE ---------------------------------------------------------------------- + +pub opaque type Msg { + GotInput(value: String) + Reset +} + +fn update(model: Model, msg: Msg) -> Model { + case msg { + GotInput(value) -> { + let length = string.length(value) + case length <= model.max { + True -> Model(..model, value: value, length: length) + False -> model + } + } + Reset -> Model(..model, value: "", length: 0) + } +} + +// VIEW ------------------------------------------------------------------------ + +fn view(model: Model) -> Element(Msg) { + let styles = [#("width", "100vw"), #("height", "100vh")] + let length = int.to_string(model.length) + + ui.centre( + [attribute.style(styles)], + ui.aside( + [aside.content_first(), aside.align_centre()], + ui.field( + [], + [element.text("Write a message:")], + ui.input([attribute.value(model.value), event.on_input(GotInput)]), + [element.text(length <> "/10")], + ), + ui.button([event.on_click(Reset)], [element.text("Reset")]), + ), + ) +} diff --git a/examples/04-http-requests/README.md b/examples/04-http-requests/README.md new file mode 100644 index 0000000..d6d82bd --- /dev/null +++ b/examples/04-http-requests/README.md @@ -0,0 +1 @@ +# 04 HTTP Requests diff --git a/examples/04-http-requests/gleam.toml b/examples/04-http-requests/gleam.toml new file mode 100644 index 0000000..b84e394 --- /dev/null +++ b/examples/04-http-requests/gleam.toml @@ -0,0 +1,12 @@ +name = "app" +version = "1.0.0" +target = "javascript" + +[dependencies] +gleam_stdlib = "~> 0.34 or ~> 1.0" +lustre = { path = "../../" } +lustre_ui = "~> 0.3" +lustre_http = "~> 0.4" + +[dev-dependencies] +gleeunit = "~> 1.0" diff --git a/examples/04-http-requests/manifest.toml b/examples/04-http-requests/manifest.toml new file mode 100644 index 0000000..fc33b28 --- /dev/null +++ b/examples/04-http-requests/manifest.toml @@ -0,0 +1,26 @@ +# This file was generated by Gleam +# You typically do not need to edit this file + +packages = [ + { name = "argv", version = "1.0.1", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "A6E9009E50BBE863EB37D963E4315398D41A3D87D0075480FC244125808F964A" }, + { name = "gleam_community_ansi", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "FE79E08BF97009729259B6357EC058315B6FBB916FAD1C2FF9355115FEB0D3A4" }, + { name = "gleam_community_colour", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "A49A5E3AE8B637A5ACBA80ECB9B1AFE89FD3D5351FF6410A42B84F666D40D7D5" }, + { name = "gleam_erlang", version = "0.24.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "26BDB52E61889F56A291CB34167315780EE4AA20961917314446542C90D1C1A0" }, + { name = "gleam_json", version = "0.7.0", build_tools = ["gleam"], requirements = ["thoas", "gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "CB405BD93A8828BCD870463DE29375E7B2D252D9D124C109E5B618AAC00B86FC" }, + { name = "gleam_otp", version = "0.9.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "gleam_erlang"], otp_app = "gleam_otp", source = "hex", outer_checksum = "5FADBBEC5ECF3F8B6BE91101D432758503192AE2ADBAD5602158977341489F71" }, + { name = "gleam_stdlib", version = "0.34.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "1FB8454D2991E9B4C0C804544D8A9AD0F6184725E20D63C3155F0AEB4230B016" }, + { name = "gleeunit", version = "1.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "D364C87AFEB26BDB4FB8A5ABDE67D635DC9FA52D6AB68416044C35B096C6882D" }, + { name = "glint", version = "0.14.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "gleam_community_colour", "gleam_community_ansi", "snag"], otp_app = "glint", source = "hex", outer_checksum = "21AB16D5A50D4EF34DF935915FDBEE06B2DAEDEE3FCC8584C6E635A866566B38" }, + { name = "lustre", version = "3.1.3", build_tools = ["gleam"], requirements = ["argv", "gleam_community_ansi", "gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib", "glint"], source = "local", path = "../.." }, + { name = "lustre_http", version = "0.4.1", build_tools = ["gleam"], requirements = ["gleam_json", "lustre", "gleam_stdlib"], otp_app = "lustre_http", source = "hex", outer_checksum = "F2E575CA598D426DECD9F617500B4EDAF11BEEF4233B8D31D2D568219E5ACCED" }, + { name = "lustre_ui", version = "0.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "gleam_community_colour", "lustre"], otp_app = "lustre_ui", source = "hex", outer_checksum = "F81BE5D20153CFFC717C2C4687A19375A8613528908AF7069DDA1B929C8398B1" }, + { name = "snag", version = "0.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "snag", source = "hex", outer_checksum = "54D32E16E33655346AA3E66CBA7E191DE0A8793D2C05284E3EFB90AD2CE92BCC" }, + { name = "thoas", version = "0.4.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "4918D50026C073C4AB1388437132C77A6F6F7C8AC43C60C13758CC0ADCE2134E" }, +] + +[requirements] +gleam_stdlib = { version = "~> 0.34 or ~> 1.0" } +gleeunit = { version = "~> 1.0" } +lustre = { path = "../../" } +lustre_http = { version = "~> 0.4"} +lustre_ui = { version = "~> 0.3" } diff --git a/examples/04-http-requests/src/app.gleam b/examples/04-http-requests/src/app.gleam new file mode 100644 index 0000000..ac75d42 --- /dev/null +++ b/examples/04-http-requests/src/app.gleam @@ -0,0 +1,91 @@ +import gleam/dynamic +import gleam/option.{type Option, None, Some} +import lustre +import lustre/attribute +import lustre/effect.{type Effect} +import lustre/element.{type Element} +import lustre/element/html +import lustre/event +// Lustre_http is a community package that provides a simple API for making +// HTTP requests from your update function. You can find the docs for the package +// here: +// +// https://hexdocs.pm/lustre_http/index.html +import lustre_http.{type HttpOrJsonError} +// These examples are written with lustre_ui in mind. They'll work regardless, +// but to see what lustre_ui can do make sure to run each of these examples with +// the `--include-styles` flag: +// +// $ gleam run -m lustre/try -- --include-styles +// +// 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 ------------------------------------------------------------------------ + +pub fn main() { + let app = lustre.application(init, update, view) + let assert Ok(_) = lustre.start(app, "#app", Nil) +} + +// MODEL ----------------------------------------------------------------------- + +type Model { + Model(quote: Option(String)) +} + +fn init(_) -> #(Model, Effect(Msg)) { + #(Model(quote: None), effect.none()) +} + +// UPDATE ---------------------------------------------------------------------- + +pub opaque type Msg { + Refresh + GotQuote(Result(String, HttpOrJsonError)) +} + +fn update(model: Model, msg: Msg) -> #(Model, Effect(Msg)) { + case msg { + Refresh -> #(model, get_quote()) + GotQuote(Ok(quote)) -> #(Model(quote: Some(quote)), effect.none()) + GotQuote(Error(_)) -> #(model, effect.none()) + } +} + +fn get_quote() -> Effect(Msg) { + let url = "https://api.quotable.io/random" + let decoder = dynamic.field("content", dynamic.string) + + lustre_http.get_as_json(url, GotQuote, decoder) +} + +// VIEW ------------------------------------------------------------------------ + +fn view(model: Model) -> Element(Msg) { + let styles = [#("width", "100vw"), #("height", "100vh")] + + ui.centre( + [attribute.style(styles)], + ui.aside( + [aside.min_width(70), attribute.style([#("width", "60ch")])], + view_quote(model.quote), + ui.button([event.on_click(Refresh)], [element.text("New quote")]), + ), + ) +} + +fn view_quote(quote: Option(String)) -> Element(msg) { + case quote { + Some(quote) -> + ui.stack([], [ + element.text("Someone once said..."), + html.p([attribute.style([#("font-style", "italic")])], [ + element.text(quote), + ]), + ]) + None -> html.p([], [element.text("Click the button to get a quote!")]) + } +} diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index 2c601ea..0000000 --- a/examples/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# Lustre examples - -Each of these examples is a complete Gleam project that contains a Lustre app -that demos or tests a particular feature of Lustre. To run any of them, navigate -to the example's directory and first run: - -```sh -$ gleam build -``` - -Then serve the app using `lustre/try`: - -```sh -$ gleam run -m lustre/try -``` - -If you do not specify a target, this will attempt to serve the app using an Erlang -HTTP server. If you'd prefer to serve using Node, you can specify the JavaScript -target instead: - -```sh -$ gleam run -m lustre/try --target javascript -``` - -Or you may additionally supply the `--runtime deno` flag to serve using Deno rather -than Node. diff --git a/examples/components/gleam.toml b/examples/components/gleam.toml deleted file mode 100644 index 3cac691..0000000 --- a/examples/components/gleam.toml +++ /dev/null @@ -1,7 +0,0 @@ -name = "components" -version = "1.0.0" -target = "javascript" - -[dependencies] -gleam_stdlib = "~> 0.34" -lustre = { path = "../../" } diff --git a/examples/components/manifest.toml b/examples/components/manifest.toml deleted file mode 100644 index 1c72364..0000000 --- a/examples/components/manifest.toml +++ /dev/null @@ -1,15 +0,0 @@ -# This file was generated by Gleam -# You typically do not need to edit this file - -packages = [ - { name = "gleam_erlang", version = "0.24.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "26BDB52E61889F56A291CB34167315780EE4AA20961917314446542C90D1C1A0" }, - { name = "gleam_json", version = "0.7.0", build_tools = ["gleam"], requirements = ["thoas", "gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "CB405BD93A8828BCD870463DE29375E7B2D252D9D124C109E5B618AAC00B86FC" }, - { name = "gleam_otp", version = "0.9.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "5FADBBEC5ECF3F8B6BE91101D432758503192AE2ADBAD5602158977341489F71" }, - { name = "gleam_stdlib", version = "0.34.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "1FB8454D2991E9B4C0C804544D8A9AD0F6184725E20D63C3155F0AEB4230B016" }, - { name = "lustre", version = "3.1.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib"], source = "local", path = "../.." }, - { name = "thoas", version = "0.4.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "4918D50026C073C4AB1388437132C77A6F6F7C8AC43C60C13758CC0ADCE2134E" }, -] - -[requirements] -gleam_stdlib = { version = "~> 0.34" } -lustre = { path = "../../" } diff --git a/examples/components/src/components.gleam b/examples/components/src/components.gleam deleted file mode 100644 index 03400bf..0000000 --- a/examples/components/src/components.gleam +++ /dev/null @@ -1,91 +0,0 @@ -// IMPORTS --------------------------------------------------------------------- - -import gleam/dynamic -import gleam/function -import gleam/int -import gleam/list -import gleam/dict -import gleam/result -import lustre -import lustre/attribute -import lustre/effect -import lustre/element.{element, text} -import lustre/element/html.{button, div, li, ol, p, slot} -import lustre/event - -// MAIN ------------------------------------------------------------------------ - -pub fn main() { - let counter = - lustre.component( - counter_init, - counter_update, - counter_view, - dict.from_list([ - #("count", fn(attr) { - dynamic.int(attr) - |> result.map(GotCount) - }), - ]), - ) - - let assert Ok(_) = lustre.register(counter, "custom-counter") - // A `simple` lustre application doesn't produce `Effect`s. These are best to - // start with if you're just getting started with lustre or you know you don't - // need the runtime to manage any side effects. - let app = lustre.simple(init, update, view) - let assert Ok(_) = lustre.start(app, "[data-lustre-app]", Nil) - - Nil -} - -fn init(_) { - [] -} - -fn update(history, msg) { - case msg { - "reset" -> [] - _ -> [msg, ..history] - } -} - -fn view(history) { - let on_custom_click = event.on("custom-click", function.constant(Ok("click"))) - - div([], [ - button([event.on_click("reset")], [text("Reset")]), - ol([], list.map(history, fn(msg) { li([], [text(msg)]) })), - element( - "custom-counter", - [on_custom_click, attribute.property("count", list.length(history))], - [ol([], list.map(history, fn(msg) { li([], [text(msg)]) }))], - ), - ]) -} - -// COUNTER --------------------------------------------------------------------- - -fn counter_init() { - #(0, effect.none()) -} - -type CounterMsg { - GotCount(Int) - Clicked -} - -fn counter_update(count, msg) { - case msg { - GotCount(count) -> #(count, effect.none()) - Clicked -> #(count, event.emit("custom-click", Nil)) - } -} - -fn counter_view(count) { - div([], [ - button([event.on_click(Clicked)], [text("Click me!")]), - p([], [text("Count: "), text(int.to_string(count))]), - slot([]), - ]) -} diff --git a/examples/counter/gleam.toml b/examples/counter/gleam.toml deleted file mode 100644 index e6bddb8..0000000 --- a/examples/counter/gleam.toml +++ /dev/null @@ -1,7 +0,0 @@ -name = "counter" -version = "1.0.0" -target = "javascript" - -[dependencies] -gleam_stdlib = "~> 0.34" -lustre = { path = "../../" } diff --git a/examples/counter/manifest.toml b/examples/counter/manifest.toml deleted file mode 100644 index 1c72364..0000000 --- a/examples/counter/manifest.toml +++ /dev/null @@ -1,15 +0,0 @@ -# This file was generated by Gleam -# You typically do not need to edit this file - -packages = [ - { name = "gleam_erlang", version = "0.24.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "26BDB52E61889F56A291CB34167315780EE4AA20961917314446542C90D1C1A0" }, - { name = "gleam_json", version = "0.7.0", build_tools = ["gleam"], requirements = ["thoas", "gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "CB405BD93A8828BCD870463DE29375E7B2D252D9D124C109E5B618AAC00B86FC" }, - { name = "gleam_otp", version = "0.9.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "5FADBBEC5ECF3F8B6BE91101D432758503192AE2ADBAD5602158977341489F71" }, - { name = "gleam_stdlib", version = "0.34.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "1FB8454D2991E9B4C0C804544D8A9AD0F6184725E20D63C3155F0AEB4230B016" }, - { name = "lustre", version = "3.1.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib"], source = "local", path = "../.." }, - { name = "thoas", version = "0.4.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "4918D50026C073C4AB1388437132C77A6F6F7C8AC43C60C13758CC0ADCE2134E" }, -] - -[requirements] -gleam_stdlib = { version = "~> 0.34" } -lustre = { path = "../../" } diff --git a/examples/counter/src/counter.gleam b/examples/counter/src/counter.gleam deleted file mode 100644 index 37af39a..0000000 --- a/examples/counter/src/counter.gleam +++ /dev/null @@ -1,53 +0,0 @@ -// IMPORTS --------------------------------------------------------------------- - -import gleam/int -import lustre -import lustre/element.{type Element, text} -import lustre/element/html.{button, div, p} -import lustre/event - -// MAIN ------------------------------------------------------------------------ - -pub fn main() { - // A `simple` lustre application doesn't produce `Effect`s. These are best to - // start with if you're just getting started with lustre or you know you don't - // need the runtime to manage any side effects. - let app = lustre.simple(init, update, view) - let assert Ok(_) = lustre.start(app, "[data-lustre-app]", Nil) -} - -// MODEL ----------------------------------------------------------------------- - -pub type Model = - Int - -pub fn init(_) -> Model { - 0 -} - -// UPDATE ---------------------------------------------------------------------- - -pub opaque type Msg { - Incr - Decr - Reset -} - -pub fn update(model: Model, msg: Msg) -> Model { - case msg { - Incr -> model + 1 - Decr -> model - 1 - Reset -> 0 - } -} - -// VIEW ------------------------------------------------------------------------ - -pub fn view(model: Model) -> Element(Msg) { - div([], [ - button([event.on_click(Incr)], [text("+")]), - button([event.on_click(Decr)], [text("-")]), - button([event.on_click(Reset)], [text("Reset")]), - p([], [text(int.to_string(model))]), - ]) -} diff --git a/examples/events/gleam.toml b/examples/events/gleam.toml deleted file mode 100644 index 46a962c..0000000 --- a/examples/events/gleam.toml +++ /dev/null @@ -1,7 +0,0 @@ -name = "events" -version = "1.0.0" -target = "javascript" - -[dependencies] -gleam_stdlib = "~> 0.34" -lustre = { path = "../../" }
\ No newline at end of file diff --git a/examples/events/manifest.toml b/examples/events/manifest.toml deleted file mode 100644 index 4116093..0000000 --- a/examples/events/manifest.toml +++ /dev/null @@ -1,15 +0,0 @@ -# This file was generated by Gleam -# You typically do not need to edit this file - -packages = [ - { name = "gleam_erlang", version = "0.24.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "26BDB52E61889F56A291CB34167315780EE4AA20961917314446542C90D1C1A0" }, - { name = "gleam_json", version = "0.7.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "CB405BD93A8828BCD870463DE29375E7B2D252D9D124C109E5B618AAC00B86FC" }, - { name = "gleam_otp", version = "0.9.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "5FADBBEC5ECF3F8B6BE91101D432758503192AE2ADBAD5602158977341489F71" }, - { name = "gleam_stdlib", version = "0.34.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "1FB8454D2991E9B4C0C804544D8A9AD0F6184725E20D63C3155F0AEB4230B016" }, - { name = "lustre", version = "3.1.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib"], source = "local", path = "../.." }, - { name = "thoas", version = "0.4.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "4918D50026C073C4AB1388437132C77A6F6F7C8AC43C60C13758CC0ADCE2134E" }, -] - -[requirements] -gleam_stdlib = { version = "~> 0.34" } -lustre = { path = "../../" } diff --git a/examples/events/src/events.gleam b/examples/events/src/events.gleam deleted file mode 100644 index 7b4a4da..0000000 --- a/examples/events/src/events.gleam +++ /dev/null @@ -1,70 +0,0 @@ -// IMPORTS --------------------------------------------------------------------- - -import gleam/int -import lustre -import lustre/attribute -import lustre/element.{type Element, text} -import lustre/element/html.{button, div, p} -import lustre/event - -// MAIN ------------------------------------------------------------------------ - -pub fn main() { - // A `simple` lustre application doesn't produce `Effect`s. These are best to - // start with if you're just getting started with lustre or you know you don't - // need the runtime to manage any side effects. - let app = lustre.simple(init, update, view) - let assert Ok(_) = lustre.start(app, "[data-lustre-app]", Nil) -} - -// MODEL ----------------------------------------------------------------------- - -pub type Model { - Model(count: Int, active: Bool) -} - -pub fn init(_) -> Model { - Model(count: 0, active: True) -} - -// UPDATE ---------------------------------------------------------------------- - -pub opaque type Msg { - Incr - Decr - Reset - Toggle -} - -pub fn update(model: Model, msg: Msg) -> Model { - case msg { - Incr -> Model(..model, count: model.count + 1) - Decr -> Model(..model, count: model.count - 1) - Reset -> Model(..model, count: 0) - Toggle -> Model(..model, active: !model.active) - } -} - -// VIEW ------------------------------------------------------------------------ - -pub fn view(model: Model) -> Element(Msg) { - let on_click = fn(msg) { - case model.active { - True -> event.on_click(msg) - False -> attribute.none() - } - } - - div([], [ - button([on_click(Incr)], [text("+")]), - button([on_click(Decr)], [text("-")]), - button([on_click(Reset)], [text("Reset")]), - button([event.on_click(Toggle)], [ - case model.active { - True -> text("Disable") - False -> text("Enable") - }, - ]), - p([], [text(int.to_string(model.count))]), - ]) -} diff --git a/examples/input/gleam.toml b/examples/input/gleam.toml deleted file mode 100644 index 5d891e8..0000000 --- a/examples/input/gleam.toml +++ /dev/null @@ -1,7 +0,0 @@ -name = "input" -version = "1.0.0" -target = "javascript" - -[dependencies] -gleam_stdlib = "~> 0.34" -lustre = { path = "../../" }
\ No newline at end of file diff --git a/examples/input/manifest.toml b/examples/input/manifest.toml deleted file mode 100644 index 16bdd27..0000000 --- a/examples/input/manifest.toml +++ /dev/null @@ -1,15 +0,0 @@ -# This file was generated by Gleam -# You typically do not need to edit this file - -packages = [ - { name = "gleam_erlang", version = "0.24.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "26BDB52E61889F56A291CB34167315780EE4AA20961917314446542C90D1C1A0" }, - { name = "gleam_json", version = "0.7.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "CB405BD93A8828BCD870463DE29375E7B2D252D9D124C109E5B618AAC00B86FC" }, - { name = "gleam_otp", version = "0.9.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "gleam_erlang"], otp_app = "gleam_otp", source = "hex", outer_checksum = "5FADBBEC5ECF3F8B6BE91101D432758503192AE2ADBAD5602158977341489F71" }, - { name = "gleam_stdlib", version = "0.34.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "1FB8454D2991E9B4C0C804544D8A9AD0F6184725E20D63C3155F0AEB4230B016" }, - { name = "lustre", version = "3.1.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib"], source = "local", path = "../.." }, - { name = "thoas", version = "0.4.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "4918D50026C073C4AB1388437132C77A6F6F7C8AC43C60C13758CC0ADCE2134E" }, -] - -[requirements] -gleam_stdlib = { version = "~> 0.34" } -lustre = { path = "../../" } diff --git a/examples/input/src/input.gleam b/examples/input/src/input.gleam deleted file mode 100644 index 6425b0c..0000000 --- a/examples/input/src/input.gleam +++ /dev/null @@ -1,120 +0,0 @@ -// IMPORTS --------------------------------------------------------------------- - -import gleam/dynamic -import gleam/string -import lustre -import lustre/attribute.{attribute} -import lustre/element.{type Element, text} -import lustre/element/html.{div, input, label, pre} -import lustre/event - -// MAIN ------------------------------------------------------------------------ - -pub fn main() { - // A `simple` lustre application doesn't produce `Effect`s. These are best to - // start with if you're just getting started with lustre or you know you don't - // need the runtime to manage any side effects. - let app = lustre.simple(init, update, view) - let assert Ok(_) = lustre.start(app, "[data-lustre-app]", Nil) - - Nil -} - -// MODEL ----------------------------------------------------------------------- - -type Model { - Model(email: String, password: String, remember_me: Bool) -} - -fn init(_) -> Model { - Model(email: "", password: "", remember_me: False) -} - -// UPDATE ---------------------------------------------------------------------- - -type Msg { - Typed(Input, String) - Toggled(Control, Bool) -} - -type Input { - Email - Password -} - -type Control { - RememberMe -} - -fn update(model: Model, msg: Msg) -> Model { - case msg { - Typed(Email, email) -> Model(..model, email: email) - Typed(Password, password) -> Model(..model, password: password) - Toggled(RememberMe, remember_me) -> Model(..model, remember_me: remember_me) - } -} - -// RENDER ---------------------------------------------------------------------- - -fn view(model: Model) -> Element(Msg) { - div([attribute.class("container")], [ - card([ - email_input(model.email), - password_input(model.password), - remember_checkbox(model.remember_me), - pre([attribute.class("debug")], [ - string.inspect(model) - |> string.replace("(", "(\n ") - |> string.replace(", ", ",\n ") - |> string.replace(")", "\n)") - |> text, - ]), - ]), - ]) -} - -fn card(content: List(Element(a))) -> Element(a) { - div([attribute.class("card")], [div([], content)]) -} - -fn email_input(value: String) -> Element(Msg) { - render_input(Email, "email", "email-input", value, "Email address") -} - -fn password_input(value: String) -> Element(Msg) { - render_input(Password, "password", "password-input", value, "Password") -} - -fn render_input( - field: Input, - type_: String, - id: String, - value: String, - label_: String, -) -> Element(Msg) { - div([attribute.class("input")], [ - label([attribute.for(id)], [text(label_)]), - input([ - attribute.id(id), - attribute.name(id), - attribute.type_(type_), - attribute.required(True), - attribute.value(dynamic.from(value)), - event.on_input(fn(value) { Typed(field, value) }), - ]), - ]) -} - -fn remember_checkbox(checked: Bool) -> Element(Msg) { - div([attribute.class("flex items-center")], [ - input([ - attribute.id("remember-me"), - attribute.name("remember-me"), - attribute.type_("checkbox"), - attribute.checked(checked), - attribute.class("checkbox"), - event.on_click(Toggled(RememberMe, !checked)), - ]), - label([attribute.for("remember-me")], [text("Remember me")]), - ]) -} diff --git a/examples/nested/gleam.toml b/examples/nested/gleam.toml deleted file mode 100644 index dd00c99..0000000 --- a/examples/nested/gleam.toml +++ /dev/null @@ -1,7 +0,0 @@ -name = "nested" -version = "1.0.0" -target = "javascript" - -[dependencies] -gleam_stdlib = "~> 0.34" -lustre = { path = "../../" }
\ No newline at end of file diff --git a/examples/nested/manifest.toml b/examples/nested/manifest.toml deleted file mode 100644 index 6593d5e..0000000 --- a/examples/nested/manifest.toml +++ /dev/null @@ -1,15 +0,0 @@ -# This file was generated by Gleam -# You typically do not need to edit this file - -packages = [ - { name = "gleam_erlang", version = "0.24.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "26BDB52E61889F56A291CB34167315780EE4AA20961917314446542C90D1C1A0" }, - { name = "gleam_json", version = "0.7.0", build_tools = ["gleam"], requirements = ["thoas", "gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "CB405BD93A8828BCD870463DE29375E7B2D252D9D124C109E5B618AAC00B86FC" }, - { name = "gleam_otp", version = "0.9.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "gleam_erlang"], otp_app = "gleam_otp", source = "hex", outer_checksum = "5FADBBEC5ECF3F8B6BE91101D432758503192AE2ADBAD5602158977341489F71" }, - { name = "gleam_stdlib", version = "0.34.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "1FB8454D2991E9B4C0C804544D8A9AD0F6184725E20D63C3155F0AEB4230B016" }, - { name = "lustre", version = "3.1.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib"], source = "local", path = "../.." }, - { name = "thoas", version = "0.4.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "4918D50026C073C4AB1388437132C77A6F6F7C8AC43C60C13758CC0ADCE2134E" }, -] - -[requirements] -gleam_stdlib = { version = "~> 0.34" } -lustre = { path = "../../" } diff --git a/examples/nested/src/nested.gleam b/examples/nested/src/nested.gleam deleted file mode 100644 index 2e1b9ec..0000000 --- a/examples/nested/src/nested.gleam +++ /dev/null @@ -1,57 +0,0 @@ -// IMPORTS --------------------------------------------------------------------- - -import nested/counter -import gleam/list -import gleam/map.{type Map} -import gleam/pair -import lustre -import lustre/element.{type Element} -import lustre/element/html.{div} - -// MAIN ------------------------------------------------------------------------ - -pub fn main() { - // A `simple` lustre application doesn't produce `Effect`s. These are best to - // start with if you're just getting started with lustre or you know you don't - // need the runtime to manage any side effects. - let app = lustre.simple(init, update, view) - let assert Ok(_) = lustre.start(app, "[data-lustre-app]", Nil) - - Nil -} - -// MODEL ----------------------------------------------------------------------- - -type Model = - Map(Int, counter.Model) - -fn init(_) -> Model { - use counters, id <- list.fold(list.range(1, 10), map.new()) - - map.insert(counters, id, counter.init(Nil)) -} - -// UPDATE ---------------------------------------------------------------------- - -type Msg = - #(Int, counter.Msg) - -fn update(model: Model, msg: Msg) -> Model { - let #(id, counter_msg) = msg - let assert Ok(counter) = map.get(model, id) - - map.insert(model, id, counter.update(counter, counter_msg)) -} - -// RENDER ---------------------------------------------------------------------- - -fn view(model: Model) -> Element(Msg) { - let counters = { - use rest, id, counter <- map.fold(model, []) - let el = element.map(counter.view(counter), pair.new(id, _)) - - [el, ..rest] - } - - div([], counters) -} diff --git a/examples/nested/src/nested/counter.gleam b/examples/nested/src/nested/counter.gleam deleted file mode 100644 index 37af39a..0000000 --- a/examples/nested/src/nested/counter.gleam +++ /dev/null @@ -1,53 +0,0 @@ -// IMPORTS --------------------------------------------------------------------- - -import gleam/int -import lustre -import lustre/element.{type Element, text} -import lustre/element/html.{button, div, p} -import lustre/event - -// MAIN ------------------------------------------------------------------------ - -pub fn main() { - // A `simple` lustre application doesn't produce `Effect`s. These are best to - // start with if you're just getting started with lustre or you know you don't - // need the runtime to manage any side effects. - let app = lustre.simple(init, update, view) - let assert Ok(_) = lustre.start(app, "[data-lustre-app]", Nil) -} - -// MODEL ----------------------------------------------------------------------- - -pub type Model = - Int - -pub fn init(_) -> Model { - 0 -} - -// UPDATE ---------------------------------------------------------------------- - -pub opaque type Msg { - Incr - Decr - Reset -} - -pub fn update(model: Model, msg: Msg) -> Model { - case msg { - Incr -> model + 1 - Decr -> model - 1 - Reset -> 0 - } -} - -// VIEW ------------------------------------------------------------------------ - -pub fn view(model: Model) -> Element(Msg) { - div([], [ - button([event.on_click(Incr)], [text("+")]), - button([event.on_click(Decr)], [text("-")]), - button([event.on_click(Reset)], [text("Reset")]), - p([], [text(int.to_string(model))]), - ]) -} diff --git a/examples/server_demo/README.md b/examples/server_demo/README.md deleted file mode 100644 index c0d6a2a..0000000 --- a/examples/server_demo/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# server_demo - -[](https://hex.pm/packages/server_demo) -[](https://hexdocs.pm/server_demo/) - -## Quick start - -```sh -gleam run # Run the project -gleam test # Run the tests -gleam shell # Run an Erlang shell -``` - -## Installation - -If available on Hex this package can be added to your Gleam project: - -```sh -gleam add server_demo -``` - -and its documentation can be found at <https://hexdocs.pm/server_demo>. diff --git a/examples/server_demo/gleam.toml b/examples/server_demo/gleam.toml deleted file mode 100644 index 81a3557..0000000 --- a/examples/server_demo/gleam.toml +++ /dev/null @@ -1,13 +0,0 @@ -name = "demo" -version = "1.0.0" - -[dependencies] -gleam_erlang = "~> 0.23" -gleam_http = "~> 3.5" -gleam_json = "~> 0.7" -gleam_otp = "~> 0.8" -gleam_stdlib = "~> 0.34" -lustre = { path = "../../" } -lustre_ui = "~> 0.2" -mist = "~> 0.15" -wisp = "~> 0.8" diff --git a/examples/server_demo/manifest.toml b/examples/server_demo/manifest.toml deleted file mode 100644 index d78134c..0000000 --- a/examples/server_demo/manifest.toml +++ /dev/null @@ -1,32 +0,0 @@ -# This file was generated by Gleam -# You typically do not need to edit this file - -packages = [ - { name = "exception", version = "1.1.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "exception", source = "hex", outer_checksum = "984401CFC95BCA87C391E36194D2B9E5B946467D44893FADB1CA4ACD4B7A29CE" }, - { name = "gleam_community_colour", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "A49A5E3AE8B637A5ACBA80ECB9B1AFE89FD3D5351FF6410A42B84F666D40D7D5" }, - { name = "gleam_crypto", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "DE1FC4E631CA374AB29CCAEAC043EE171B86114D7DC66DD483F0A93BF0C4C6FF" }, - { name = "gleam_erlang", version = "0.24.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "26BDB52E61889F56A291CB34167315780EE4AA20961917314446542C90D1C1A0" }, - { name = "gleam_http", version = "3.5.3", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "C2FC3322203B16F897C1818D9810F5DEFCE347F0751F3B44421E1261277A7373" }, - { name = "gleam_json", version = "0.7.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "CB405BD93A8828BCD870463DE29375E7B2D252D9D124C109E5B618AAC00B86FC" }, - { name = "gleam_otp", version = "0.9.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "5FADBBEC5ECF3F8B6BE91101D432758503192AE2ADBAD5602158977341489F71" }, - { name = "gleam_stdlib", version = "0.34.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "1FB8454D2991E9B4C0C804544D8A9AD0F6184725E20D63C3155F0AEB4230B016" }, - { name = "glisten", version = "0.9.2", build_tools = ["gleam"], requirements = ["gleam_stdlib", "gleam_erlang", "gleam_otp"], otp_app = "glisten", source = "hex", outer_checksum = "C960B6CF25D4AABAB01211146E9B57E11827B9C49E4175217E0FB7EF5BCB0FF7" }, - { name = "lustre", version = "3.1.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib"], source = "local", path = "../.." }, - { name = "lustre_ui", version = "0.3.0", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_stdlib", "lustre"], otp_app = "lustre_ui", source = "hex", outer_checksum = "F81BE5D20153CFFC717C2C4687A19375A8613528908AF7069DDA1B929C8398B1" }, - { name = "marceau", version = "1.1.0", build_tools = ["gleam"], requirements = [], otp_app = "marceau", source = "hex", outer_checksum = "1AAD727A30BE0F95562C3403BB9B27C823797AD90037714255EEBF617B1CDA81" }, - { name = "mist", version = "0.15.0", build_tools = ["gleam"], requirements = ["gleam_otp", "gleam_erlang", "gleam_stdlib", "gleam_http", "glisten"], otp_app = "mist", source = "hex", outer_checksum = "49F51DDB64D7B2832F72727CC9721C478D6B524C96EA444C601A19D01E023C03" }, - { name = "simplifile", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "359CD7006E2F69255025C858CCC6407C11A876EC179E6ED1E46809E8DC6B1AAD" }, - { name = "thoas", version = "0.4.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "4918D50026C073C4AB1388437132C77A6F6F7C8AC43C60C13758CC0ADCE2134E" }, - { name = "wisp", version = "0.10.0", build_tools = ["gleam"], requirements = ["marceau", "gleam_json", "simplifile", "gleam_http", "gleam_crypto", "mist", "gleam_stdlib", "exception", "gleam_erlang"], otp_app = "wisp", source = "hex", outer_checksum = "744FF91702078301BDF8FE76F26C14B658A7D151D867FA6995762318ED2536A0" }, -] - -[requirements] -gleam_erlang = { version = "~> 0.23" } -gleam_http = { version = "~> 3.5" } -gleam_json = { version = "~> 0.7" } -gleam_otp = { version = "~> 0.8" } -gleam_stdlib = { version = "~> 0.34" } -lustre = { path = "../../" } -lustre_ui = { version = "~> 0.2" } -mist = { version = "~> 0.15" } -wisp = { version = "~> 0.8" } diff --git a/examples/server_demo/src/demo.gleam b/examples/server_demo/src/demo.gleam deleted file mode 100644 index ebe858a..0000000 --- a/examples/server_demo/src/demo.gleam +++ /dev/null @@ -1,22 +0,0 @@ -// IMPORTS --------------------------------------------------------------------- - -import demo/socket -import demo/web -import gleam/erlang/process -import mist - -// MAIN ------------------------------------------------------------------------ - -pub fn main() { - let assert Ok(_) = - mist.new(fn(req) { - case req.path { - "/ws" -> socket.handle(req) - _ -> web.handle(req) - } - }) - |> mist.port(8000) - |> mist.start_http - - process.sleep_forever() -} diff --git a/examples/server_demo/src/demo/app.gleam b/examples/server_demo/src/demo/app.gleam deleted file mode 100644 index 13d09a8..0000000 --- a/examples/server_demo/src/demo/app.gleam +++ /dev/null @@ -1,88 +0,0 @@ -// IMPORTS --------------------------------------------------------------------- - -import gleam/dict.{type Dict} -import gleam/dynamic.{type Decoder} -import gleam/int -import gleam/json -import gleam/result -import lustre/attribute -import lustre/effect.{type Effect} -import lustre/element.{type Element} -import lustre/element/html -import lustre/event -import lustre/server -import lustre/ui - -// MODEL ----------------------------------------------------------------------- - -pub type Model = - Int - -pub fn init(count: Int) -> #(Model, Effect(Msg)) { - #(count, effect.none()) -} - -// UPDATE ---------------------------------------------------------------------- - -pub opaque type Msg { - Incr - Decr - Reset(Int) -} - -pub fn update(model: Model, msg: Msg) -> #(Model, Effect(Msg)) { - case msg { - Incr -> #(model + 1, effect.none()) - Decr -> #(model - 1, effect.none()) - Reset(count) -> #( - count, - effect.event( - "changed", - json.string("You reset the count to: " <> int.to_string(count)), - ), - ) - } -} - -pub fn on_attribute_change() -> Dict(String, Decoder(Msg)) { - dict.from_list([ - #("count", fn(dyn) { - dyn - |> dynamic.int - |> result.map(Reset) - }), - ]) -} - -// VIEW ------------------------------------------------------------------------ - -pub fn view(model: Model) -> Element(Msg) { - let count = int.to_string(model) - - ui.centre( - [attribute.style([#("width", "100vw"), #("height", "100vh")])], - ui.sequence([], [ - ui.button([event.on_click(Decr)], [element.text("-")]), - ui.centre([], html.span([], [element.text(count)])), - ui.button([event.on_click(Incr)], [element.text("+")]), - ]), - ) - // ui.cluster([], [ - // ui.input([event.on_input(Change), attribute.value(model.input)]), - // html.span([], [element.text(model.input)]), - // ]), - // ui.centre( - // [ - // event.on("mousemove", on_mouse_move), - // server.include(["offsetX", "offsetY"]), - // attribute.style([ - // #("aspect-ratio", "1 / 1 "), - // #("background-color", "var(--element-background)"), - // ]), - // ], - // html.div([], [ - // html.p([], [element.text("x: " <> int.to_string(model.mouse.0))]), - // html.p([], [element.text("y: " <> int.to_string(model.mouse.1))]), - // ]), - // ), -} diff --git a/examples/server_demo/src/demo/socket.gleam b/examples/server_demo/src/demo/socket.gleam deleted file mode 100644 index bc43962..0000000 --- a/examples/server_demo/src/demo/socket.gleam +++ /dev/null @@ -1,78 +0,0 @@ -// IMPORTS --------------------------------------------------------------------- - -import demo/app -import gleam/bit_array -import gleam/erlang/process.{type Subject} -import gleam/function.{identity} -import gleam/http/request.{type Request as HttpRequest} -import gleam/http/response.{type Response as HttpResponse} -import gleam/json -import gleam/option.{Some} -import gleam/otp/actor -import gleam/result -import lustre.{type Action, type ServerComponent} -import lustre/element.{type Patch} -import lustre/server -import mist.{ - type Connection, type ResponseData, type WebsocketConnection, - type WebsocketMessage, -} - -// - -pub fn handle(req: HttpRequest(Connection)) -> HttpResponse(ResponseData) { - mist.websocket(req, update, init, function.constant(Nil)) -} - -type Model(flags, model, msg) { - Model(self: Subject(Patch(msg)), app: Subject(Action(ServerComponent, msg))) -} - -fn init(_) { - let assert Ok(app) = - lustre.component(app.init, app.update, app.view, app.on_attribute_change()) - |> lustre.start_actor(0) - let self = process.new_subject() - let model = Model(self, app) - let selector = process.selecting(process.new_selector(), self, identity) - - actor.send(app, lustre.add_renderer(process.self(), process.send(self, _))) - #(model, Some(selector)) -} - -fn update( - model: Model(flags, model, msg), - conn: WebsocketConnection, - msg: WebsocketMessage(Patch(a)), -) { - case msg { - mist.Text(bits) -> { - let _ = { - use dyn <- json.decode_bits(bits) - use action <- result.try(server.decode_action(dyn)) - - actor.send(model.app, action) - Ok(Nil) - } - - actor.continue(model) - } - mist.Binary(_) -> actor.continue(model) - mist.Closed -> { - actor.send(model.app, lustre.remove_renderer(process.self())) - actor.continue(model) - } - mist.Shutdown -> { - actor.send(model.app, lustre.shutdown()) - actor.Stop(process.Normal) - } - mist.Custom(patch) -> { - element.encode_patch(patch) - |> json.to_string - |> bit_array.from_string - |> mist.send_text_frame(conn, _) - - actor.continue(model) - } - } -} diff --git a/examples/server_demo/src/demo/web.gleam b/examples/server_demo/src/demo/web.gleam deleted file mode 100644 index b8c17e2..0000000 --- a/examples/server_demo/src/demo/web.gleam +++ /dev/null @@ -1,58 +0,0 @@ -// IMPORTS --------------------------------------------------------------------- - -import gleam/http/request.{type Request as HttpRequest} -import gleam/http/response.{type Response as HttpResponse} -import gleam/result -import gleam/string_builder -import lustre/attribute.{attribute} -import lustre/element -import lustre/element/html.{html} -import lustre/server -import lustre/ui/styles -import mist.{type Connection, type ResponseData} -import wisp.{type Request, type Response} - -// - -pub fn handle(req: HttpRequest(Connection)) -> HttpResponse(ResponseData) { - wisp.mist_handler(handler, "")(req) -} - -fn handler(req: Request) -> Response { - use req <- wisp.handle_head(req) - use <- wisp.serve_static(req, under: "/static", from: static_directory()) - - html([attribute("lang", "en")], [ - html.head([], [ - html.meta([attribute("charset", "utf-8")]), - html.meta([ - attribute("name", "viewport"), - attribute("content", "width=device-width, initial-scale=1"), - ]), - styles.elements(), - html.script( - [attribute("type", "module")], - " - import '/static/lustre-server-component.mjs' - - document.addEventListener('DOMContentLoaded', () => { - document - .querySelector('lustre-server-component') - .addEventListener('alert', event => { - console.log(`The server count says: ${event.detail}`) - }) - }) - ", - ), - ]), - html.body([], [server.component([server.route("/ws")])]), - ]) - |> element.to_string_builder - |> string_builder.prepend("<!DOCTYPE html>\n") - |> wisp.html_response(200) -} - -fn static_directory() -> String { - wisp.priv_directory("lustre") - |> result.unwrap("") -} diff --git a/examples/svg/gleam.toml b/examples/svg/gleam.toml deleted file mode 100644 index b755f57..0000000 --- a/examples/svg/gleam.toml +++ /dev/null @@ -1,7 +0,0 @@ -name = "svg" -version = "1.0.0" -target = "javascript" - -[dependencies] -gleam_stdlib = "~> 0.34" -lustre = { path = "../../" }
\ No newline at end of file diff --git a/examples/svg/manifest.toml b/examples/svg/manifest.toml deleted file mode 100644 index 1c72364..0000000 --- a/examples/svg/manifest.toml +++ /dev/null @@ -1,15 +0,0 @@ -# This file was generated by Gleam -# You typically do not need to edit this file - -packages = [ - { name = "gleam_erlang", version = "0.24.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "26BDB52E61889F56A291CB34167315780EE4AA20961917314446542C90D1C1A0" }, - { name = "gleam_json", version = "0.7.0", build_tools = ["gleam"], requirements = ["thoas", "gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "CB405BD93A8828BCD870463DE29375E7B2D252D9D124C109E5B618AAC00B86FC" }, - { name = "gleam_otp", version = "0.9.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "5FADBBEC5ECF3F8B6BE91101D432758503192AE2ADBAD5602158977341489F71" }, - { name = "gleam_stdlib", version = "0.34.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "1FB8454D2991E9B4C0C804544D8A9AD0F6184725E20D63C3155F0AEB4230B016" }, - { name = "lustre", version = "3.1.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib"], source = "local", path = "../.." }, - { name = "thoas", version = "0.4.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "4918D50026C073C4AB1388437132C77A6F6F7C8AC43C60C13758CC0ADCE2134E" }, -] - -[requirements] -gleam_stdlib = { version = "~> 0.34" } -lustre = { path = "../../" } diff --git a/examples/svg/src/svg.gleam b/examples/svg/src/svg.gleam deleted file mode 100644 index 93be5e3..0000000 --- a/examples/svg/src/svg.gleam +++ /dev/null @@ -1,103 +0,0 @@ -// IMPORTS --------------------------------------------------------------------- - -import gleam/int -import lustre -import lustre/attribute.{attribute} -import lustre/element.{type Element, text} -import lustre/element/html.{button, div, p, svg} -import lustre/element/svg.{path} -import lustre/event - -// MAIN ------------------------------------------------------------------------ - -pub fn main() { - // A `simple` lustre application doesn't produce `Effect`s. These are best to - // start with if you're just getting started with lustre or you know you don't - // need the runtime to manage any side effects. - let app = lustre.simple(init, update, view) - let assert Ok(_) = lustre.start(app, "[data-lustre-app]", Nil) -} - -// MODEL ----------------------------------------------------------------------- - -pub type Model = - Int - -pub fn init(_) -> Model { - 0 -} - -// UPDATE ---------------------------------------------------------------------- - -pub opaque type Msg { - Incr - Decr - Reset -} - -pub fn update(model: Model, msg: Msg) -> Model { - case msg { - Incr -> model + 1 - Decr -> model - 1 - Reset -> 0 - } -} - -// VIEW ------------------------------------------------------------------------ - -pub fn view(model: Model) -> Element(Msg) { - div([], [ - button([event.on_click(Incr)], [ - plus([attribute.style([#("color", "red")])]), - ]), - button([event.on_click(Decr)], [minus([])]), - button([event.on_click(Reset)], [text("Reset")]), - p([], [text(int.to_string(model))]), - ]) -} - -fn plus(attrs) { - svg( - [ - attribute("width", "15"), - attribute("height", "15"), - attribute("viewBox", "0 0 15 15"), - attribute("fill", "none"), - ..attrs - ], - [ - path([ - attribute( - "d", - "M8 2.75C8 2.47386 7.77614 2.25 7.5 2.25C7.22386 2.25 7 2.47386 7 2.75V7H2.75C2.47386 7 2.25 7.22386 2.25 7.5C2.25 7.77614 2.47386 8 2.75 8H7V12.25C7 12.5261 7.22386 12.75 7.5 12.75C7.77614 12.75 8 12.5261 8 12.25V8H12.25C12.5261 8 12.75 7.77614 12.75 7.5C12.75 7.22386 12.5261 7 12.25 7H8V2.75Z", - ), - attribute("fill", "currentColor"), - attribute("fill-rule", "evenodd"), - attribute("clip-rule", "evenodd"), - ]), - ], - ) -} - -fn minus(attrs) { - svg( - [ - attribute("width", "15"), - attribute("height", "15"), - attribute("viewBox", "0 0 15 15"), - attribute("fill", "none"), - ..attrs - ], - [ - path([ - attribute( - "d", - "M2.25 7.5C2.25 7.22386 2.47386 7 2.75 7H12.25C12.5261 7 12.75 7.22386 12.75 7.5C12.75 7.77614 12.5261 8 12.25 8H2.75C2.47386 8 2.25 7.77614 2.25 7.5Z", - ), - attribute("fill", "currentColor"), - attribute("fill-rule", "evenodd"), - attribute("clip-rule", "evenodd"), - ]), - ], - ) -} |