aboutsummaryrefslogtreecommitdiff
path: root/src/lustre.gleam
diff options
context:
space:
mode:
authorHayleigh Thompson <me@hayleigh.dev>2023-02-13 13:13:56 +0000
committerHayleigh Thompson <me@hayleigh.dev>2023-02-13 13:13:56 +0000
commit44ebb3d87738847f9363f46e7027a5b022144a5c (patch)
treeff8b0092c35729fa899db0240f6b12518bcaefc0 /src/lustre.gleam
parent243b746be1faa2b3c089e4599cdf32431561f418 (diff)
downloadlustre-44ebb3d87738847f9363f46e7027a5b022144a5c.tar.gz
lustre-44ebb3d87738847f9363f46e7027a5b022144a5c.zip
:memo: Docs for the docs god.
Diffstat (limited to 'src/lustre.gleam')
-rw-r--r--src/lustre.gleam328
1 files changed, 176 insertions, 152 deletions
diff --git a/src/lustre.gleam b/src/lustre.gleam
index 2455da6..bdf6225 100644
--- a/src/lustre.gleam
+++ b/src/lustre.gleam
@@ -1,17 +1,15 @@
//// Lustre is a declarative framework for building Web apps in Gleam.
-
// IMPORTS ---------------------------------------------------------------------
-import lustre/cmd.{ Cmd }
-import lustre/element.{ Element }
+import lustre/cmd.{Cmd}
+import lustre/element.{Element}
import gleam/result
-
// TYPES -----------------------------------------------------------------------
/// An `App` describes a Lustre application: what state it holds and what kind
-/// of actions get dispatched to update that state. The only useful thing you can
+/// of actions get dispatched to update that model. The only useful thing you can
/// do with an `App` is pass it to [`start`](#start).
///
/// You can construct an `App` from the two constructors exposed in this module:
@@ -20,47 +18,47 @@ import gleam/result
/// you want to set up an application but defer starting it until some later point
/// in time.
///
-///```
-/// +--------+
-/// | |
-/// | update |
-/// | |
-/// +--------+
-/// ^ |
-/// | |
-/// Action | | #(State, Action)
-/// | |
-/// | v
-/// +------+ +------------------------+
-/// | | #(State, Action) | |
-/// | init |------------------->| Lustre Runtime |
-/// | | | |
-/// +------+ +------------------------+
-/// ^ |
-/// | |
-/// Action | | State
-/// | |
-/// | v
-/// +--------+
-/// | |
-/// | render |
-/// | |
-/// +--------+
-///```
+/// ```text
+/// +--------+
+/// | |
+/// | update |
+/// | |
+/// +--------+
+/// ^ |
+/// | |
+/// Msg | | #(Model, Cmd(Msg))
+/// | |
+/// | v
+/// +------+ +------------------------+
+/// | | #(Model, Cmd(Msg)) | |
+/// | init |--------------------->| Lustre Runtime |
+/// | | | |
+/// +------+ +------------------------+
+/// ^ |
+/// | |
+/// Msg | | Model
+/// | |
+/// | v
+/// +--------+
+/// | |
+/// | render |
+/// | |
+/// +--------+
+/// ```
///
/// <small>Someone please PR the Gleam docs generator to fix the monospace font,
/// thanks! 💖</small>
///
-pub opaque type App(state, action) {
- App(
- init: #(state, Cmd(action)),
- update: Update(state, action),
- render: Render(state, action)
- )
+pub opaque type App(model, msg) {
+ App(
+ init: #(model, Cmd(msg)),
+ update: Update(model, msg),
+ render: Render(model, msg),
+ )
}
pub type Error {
- ElementNotFound
+ ElementNotFound
}
// These types aren't exposed, but they're just here to try and shrink the type
@@ -68,9 +66,12 @@ pub type Error {
// Gleam automatically expands type aliases so this is purely for the benefit of
// those reading the source.
//
-type Update(state, action) = fn (state, action) -> #(state, Cmd(action))
-type Render(state, action) = fn (state) -> Element(action)
+type Update(model, msg) =
+ fn(model, msg) -> #(model, Cmd(msg))
+
+type Render(model, msg) =
+ fn(model) -> Element(msg)
// CONSTRUCTORS ----------------------------------------------------------------
@@ -83,78 +84,87 @@ type Render(state, action) = fn (state) -> Element(action)
/// you might want to consider using [`simple`](#simple) or [`application`](#application)
/// instead.
///
-///```gleam
-///import lustre
-///import lustre/element
-///
-///pub fn main () {
-/// let app = lustre.element(
-/// element.h1([], [
-/// element.text("Hello, world!")
-/// ])
-/// )
+/// ```gleam
+/// import lustre
+/// import lustre/element
+///
+/// pub fn main () {
+/// let app = lustre.element(
+/// element.h1([], [
+/// element.text("Hello, world!")
+/// ])
+/// )
+///
+/// assert Ok(_) = lustre.start(app, "#root")
+/// }
+/// ```
///
-/// lustre.start(app, "#root")
-///}
-///```
-///
-pub fn element (element: Element(any)) -> App(Nil, any) {
- let init = #(Nil, cmd.none())
- let update = fn (_, _) { #(Nil, cmd.none()) }
- let render = fn (_) { element }
+pub fn element(element: Element(msg)) -> App(Nil, msg) {
+ let init = #(Nil, cmd.none())
+ let update = fn(_, _) { #(Nil, cmd.none()) }
+ let render = fn(_) { element }
- App(init, update, render)
+ App(init, update, render)
}
/// If you start off with a simple `[element`](#element) app, you may find
/// yourself leaning on [`stateful`](./lustrel/element.html#stateful) elements
-/// to manage state used throughout your app. If that's the case or if you know
-/// you need some global state from the get-go, you might want to construct a
+/// to manage model used throughout your app. If that's the case or if you know
+/// you need some global model from the get-go, you might want to construct a
/// [`simple`](#simple) app instead.
///
/// This is one app constructor that allows your HTML elements to dispatch actions
-/// to update your program state.
+/// to update your program model.
///
-///```
-///import gleam/int
-///import lustre
-///import lustre/element
-///import lustre/event.{ dispatch }
-///
-///type Action {
-/// Incr
-/// Decr
-///}
-///
-///pub fn main () {
-/// let init = 0
-/// let update = fn (state, action) {
-/// case action {
-/// Incr -> state + 1
-/// Decr -> state - 1
-/// }
-/// }
-/// let render = fn (state) {
-/// element.div([], [
-/// element.button([ event.on_click(dispatch(Decr)) ], [
-/// element.text("-")
-/// ]),
-/// element.text(state |> int.to_string |> element.text),
-/// element.button([ event.on_click(dispatch(Incr)) ], [
-/// element.text("+")
-/// ])
-/// ])
-/// }
-///
-/// let app = lustre.simple(init, update, render)
-/// lustre.start(app, "#root")
-///}
-///```
-pub fn simple (init: state, update: fn (state, action) -> state, render: fn (state) -> Element(action)) -> App(state, action) {
- let init = #(init, cmd.none())
- let update = fn (state, action) { #(update(state, action), cmd.none()) }
+/// ```gleam
+/// import gleam/int
+/// import lustre
+/// import lustre/element
+/// import lustre/event
+///
+/// type Msg {
+/// Decr
+/// Incr
+/// }
+///
+/// pub fn main () {
+/// let init = 0
+///
+/// let update = fn (model, msg) {
+/// case msg {
+/// Decr -> model - 1
+/// Incr -> model + 1
+/// }
+/// }
+///
+/// let render = fn (model) {
+/// element.div([], [
+/// element.button([ event.on_click(Decr) ], [
+/// element.text("-")
+/// ]),
+///
+/// element.text(int.to_string(model)),
+///
+/// element.button([ event.on_click(Incr) ], [
+/// element.text("+")
+/// ])
+/// ])
+/// }
+///
+/// let app = lustre.simple(init, update, render)
+/// assert Ok(_) = lustre.start(app, "#root")
+/// }
+/// ```
+///
+pub fn simple(
+ init: model,
+ update: fn(model, msg) -> model,
+ render: fn(model) -> Element(msg),
+) -> App(model, msg) {
+ let init = #(init, cmd.none())
+ let update = fn(model, msg) { #(update(model, msg), cmd.none()) }
- App(init, update, render)
+ App(init, update, render)
}
/// An evolution of a [`simple`](#simple) app that allows you to return a
@@ -163,44 +173,49 @@ pub fn simple (init: state, update: fn (state, action) -> state, render: fn (sta
/// timer and then dispatch actions back to the runtime to trigger an `update`.
///
///```
-///import lustre
-///import lustre/cmd
-///import lustre/element
-///
-///pub fn main () {
-/// let init = #(0, tick())
-/// let update = fn (state, action) {
-/// case action {
-/// Tick -> #(state + 1, tick())
-/// }
-/// }
-/// let render = fn (state) {
-/// element.div([], [
-/// element.text("Count is: ")
-/// element.text(state |> int.to_string |> element.text)
-/// ])
-/// }
-///
-/// let app = lustre.simple(init, update, render)
-/// lustre.start(app, "#root")
-///}
-///
-///fn tick () -> Cmd(Action) {
-/// cmd.from(fn (dispatch) {
-/// setInterval(fn () {
-/// dispatch(Tick)
-/// }, 1000)
-/// })
-///}
-///
-///external fn set_timeout (f: fn () -> a, delay: Int) -> Nil
-/// = "" "window.setTimeout"
+/// import lustre
+/// import lustre/cmd
+/// import lustre/element
+///
+/// pub fn main () {
+/// let init = #(0, tick())
+///
+/// let update = fn (model, msg) {
+/// case msg {
+/// Tick -> #(model + 1, tick())
+/// }
+/// }
+///
+/// let render = fn (model) {
+/// element.div([], [
+/// element.text("Time elapsed: ")
+/// element.text(int.to_string(model))
+/// ])
+/// }
+///
+/// let app = lustre.simple(init, update, render)
+/// assert Ok(_) = lustre.start(app, "#root")
+/// }
+///
+/// fn tick () -> Cmd(Msg) {
+/// cmd.from(fn (dispatch) {
+/// setInterval(fn () {
+/// dispatch(Tick)
+/// }, 1000)
+/// })
+/// }
+///
+/// external fn set_timeout (f: fn () -> a, delay: Int) -> Nil
+/// = "" "window.setTimeout"
///```
-pub fn application (init: #(state, Cmd(action)), update: Update(state, action), render: Render(state, action)) -> App(state, action) {
- App(init, update, render)
+pub fn application(
+ init: #(model, Cmd(msg)),
+ update: Update(model, msg),
+ render: Render(model, msg),
+) -> App(model, msg) {
+ App(init, update, render)
}
-
// EFFECTS ---------------------------------------------------------------------
/// Once you have created a app with either `basic` or `application`, you
@@ -211,23 +226,32 @@ pub fn application (init: #(state, Cmd(action)), update: Update(state, action),
/// call to send actions to your app and trigger an update.
///
///```
-///import lustre
-///
-///pub fn main () {
-/// let app = lustre.appliation(init, update, render)
-/// assert Ok(dispatch) = lustre.start(app, "#root")
+/// import lustre
///
-/// dispatch(Incr)
-/// dispatch(Incr)
-/// dispatch(Incr)
-///}
+/// pub fn main () {
+/// let app = lustre.appliation(init, update, render)
+/// assert Ok(dispatch) = lustre.start(app, "#root")
+///
+/// dispatch(Incr)
+/// dispatch(Incr)
+/// dispatch(Incr)
+/// }
///```
///
-pub fn start (app: App(state, action), selector: String) -> Result(fn (action) -> Nil, Error) {
- mount(app, selector)
- |> result.replace_error(ElementNotFound)
+/// This may not seem super useful at first, but by returning this dispatch
+/// function from your `main` (or elsewhere) you can get events into your Lustre
+/// app from the outside world.
+///
+pub fn start(
+ app: App(model, msg),
+ selector: String,
+) -> Result(fn(msg) -> Nil, Error) {
+ mount(app, selector)
+ |> result.replace_error(ElementNotFound)
}
-
-external fn mount (app: App(state, action), selector: String) -> Result(fn (action) -> Nil, Nil)
- = "./ffi.mjs" "mount"
+external fn mount(
+ app: App(model, msg),
+ selector: String,
+) -> Result(fn(msg) -> Nil, Nil) =
+ "./ffi.mjs" "mount"