aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--gleam.toml32
-rw-r--r--src/lustre/attribute.gleam27
-rw-r--r--src/lustre/effect.gleam23
-rw-r--r--src/lustre/element.gleam72
-rw-r--r--src/lustre/element/html.gleam235
-rw-r--r--src/lustre/element/svg.gleam3
-rw-r--r--src/lustre/event.gleam45
-rw-r--r--src/lustre/internals/runtime.gleam39
9 files changed, 262 insertions, 216 deletions
diff --git a/README.md b/README.md
index c10b38e..e297d56 100644
--- a/README.md
+++ b/README.md
@@ -19,10 +19,12 @@
<div align="center">
<h3>
+ <!--
<a href="https://lustre.build">
Website
</a>
<span> | </span>
+ -->
<a href="https://github.com/lustre-labs/lustre/docs/guide/01-quickstart.md">
Quickstart
</a>
diff --git a/gleam.toml b/gleam.toml
index 8acddac..b5cc6b9 100644
--- a/gleam.toml
+++ b/gleam.toml
@@ -7,7 +7,7 @@ repository = { type = "github", user = "lustre-labs", repo = "lustre" }
licences = ["MIT"]
links = [
- { title = "Website", href = "https://lustre.build" },
+ # { title = "Website", href = "https://lustre.build" },
{ title = "Examples", href = "https://github.com/lustre-labs/lustre/tree/main/examples" },
{ title = "Sponsor", href = "https://github.com/sponsors/hayleigh-dot-dev" },
]
@@ -21,22 +21,22 @@ internal_modules = [
[documentation]
pages = [
- { title = "CLI reference", path = "#", source = "" },
- { title = " ", path = "#", source = "" },
+ # { title = "CLI reference", path = "#", source = "" },
+ # { title = " ", path = "#", source = "" },
{ title = "Quickstart guide", path = "guide/01-quickstart.html", source = "./docs/guide/01-quickstart.md" },
- { title = "Managing state", path = "#", source = "" },
- { title = "Side effects", path = "#", source = "" },
- { title = "Server-side rendering", path = "#", source = "" },
- { title = "Components", path = "#", source = "" },
- { title = "Server components", path = "#", source = "" },
- { title = " ", path = "#", source = "" },
- { title = "Using with Wisp", path = "#", source = "" },
- { title = "Using with Glen", path = "#", source = "" },
- { title = "Using with Mist", path = "#", source = "" },
- { title = " ", path = "#", source = "" },
- { title = "For Elm developers", path = "#", source = "" },
- { title = "For React developers", path = "#", source = "" },
- { title = "For LiveView developers", path = "#", source = "" },
+ # { title = "Managing state", path = "#", source = "" },
+ # { title = "Side effects", path = "#", source = "" },
+ # { title = "Server-side rendering", path = "#", source = "" },
+ # { title = "Components", path = "#", source = "" },
+ # { title = "Server components", path = "#", source = "" },
+ # { title = " ", path = "#", source = "" },
+ # { title = "Using with Wisp", path = "#", source = "" },
+ # { title = "Using with Glen", path = "#", source = "" },
+ # { title = "Using with Mist", path = "#", source = "" },
+ # { title = " ", path = "#", source = "" },
+ # { title = "For Elm developers", path = "#", source = "" },
+ # { title = "For React developers", path = "#", source = "" },
+ # { title = "For LiveView developers", path = "#", source = "" },
]
[dependencies]
diff --git a/src/lustre/attribute.gleam b/src/lustre/attribute.gleam
index 0ae289c..7d49b18 100644
--- a/src/lustre/attribute.gleam
+++ b/src/lustre/attribute.gleam
@@ -1,6 +1,3 @@
-//// To read the full documentation for this module, please visit
-//// [https://lustre.build/api/lustre/attribute](https://lustre.build/api/lustre/attribute)
-
// IMPORTS ---------------------------------------------------------------------
import gleam/dynamic.{type Dynamic}
@@ -13,7 +10,8 @@ import lustre/internals/vdom.{Attribute, Event}
// TYPES -----------------------------------------------------------------------
-///
+/// The `Attribute` type encompasses HTML attributes, DOM properties, and
+/// event listeners.
pub type Attribute(msg) =
vdom.Attribute(msg)
@@ -24,7 +22,7 @@ pub fn attribute(name: String, value: String) -> Attribute(msg) {
Attribute(name, dynamic.from(value), as_property: False)
}
-///
+///
pub fn property(name: String, value: any) -> Attribute(msg) {
Attribute(name, dynamic.from(value), as_property: True)
}
@@ -39,12 +37,16 @@ pub fn on(
///
pub fn none() -> Attribute(msg) {
- Attribute("", dynamic.from(""), as_property: False)
+ class("")
}
// MANIPULATIONS ---------------------------------------------------------------
-///
+/// The `Attribute` type is parameterised by the type of messages it can produce
+/// from events handlers. Sometimes you might end up with an attribute from a
+/// library or module that produces a different type of message: this function lets
+/// you map the messages produced from one type to another.
+///
pub fn map(attr: Attribute(a), f: fn(a) -> b) -> Attribute(b) {
case attr {
Attribute(name, value, as_property) -> Attribute(name, value, as_property)
@@ -55,6 +57,11 @@ pub fn map(attr: Attribute(a), f: fn(a) -> b) -> Attribute(b) {
// COMMON ATTRIBUTES -----------------------------------------------------------
///
+///
+/// **Note**: unlike most attributes, multiple `style` attributes are merged
+/// with any existing other styles on an element. Styles added _later_ in the
+/// list will override styles added earlier.
+///
pub fn style(properties: List(#(String, String))) -> Attribute(msg) {
attribute("style", {
use styles, #(name, value) <- list.fold(properties, "")
@@ -63,6 +70,10 @@ pub fn style(properties: List(#(String, String))) -> Attribute(msg) {
}
///
+///
+/// **Note**: unlike most attributes, multiple `class` attributes are merged
+/// with any existing other classes on an element.
+///
pub fn class(name: String) -> Attribute(msg) {
attribute("class", name)
}
@@ -100,7 +111,7 @@ pub fn type_(name: String) -> Attribute(msg) {
}
///
-pub fn value(val: any) -> Attribute(msg) {
+pub fn value(val: String) -> Attribute(msg) {
property("value", dynamic.from(val))
}
diff --git a/src/lustre/effect.gleam b/src/lustre/effect.gleam
index d92a2f1..38f809f 100644
--- a/src/lustre/effect.gleam
+++ b/src/lustre/effect.gleam
@@ -54,7 +54,7 @@ import gleam/function
///
/// 2. The type of messages that (might) be sent back to the program in response.
///
-///
+///
///
pub opaque type Effect(msg) {
Effect(all: List(fn(fn(msg) -> Nil, fn(String, Json) -> Nil) -> Nil))
@@ -80,8 +80,8 @@ pub fn from(effect: fn(fn(msg) -> Nil) -> Nil) -> Effect(msg) {
/// of Lustre's components, but in rare cases it may be useful to emit custom
/// events from the DOM node that your Lustre application is mounted to.
///
-///
-///
+///
+///
pub fn event(name: String, data: Json) -> Effect(msg) {
Effect([fn(_, emit) { emit(name, data) }])
}
@@ -97,14 +97,14 @@ pub fn none() -> Effect(msg) {
// MANIPULATIONS ---------------------------------------------------------------
/// Batch multiple effects to be performed at the same time.
-///
+///
/// **Note:** The runtime makes no guarantees about the order on which effects
/// are performed! If you need to chain or sequence effects together, you have
/// two broad options:
-///
+///
/// 1. Create variants of your `msg` type to represent each step in the sequence
/// and fire off the next effect in response to the previous one.
-///
+///
/// 2. If you're defining effects yourself, consider whether or not you can handle
/// the sequencing inside the effect itself.
///
@@ -117,7 +117,7 @@ pub fn batch(effects: List(Effect(msg))) -> Effect(msg) {
/// Transform the result of an effect. This is useful for mapping over effects
/// produced by other libraries or modules.
-///
+///
/// **Note:** Remember that effects are not _required_ to dispatch any messages.
/// Your mapping function may never be called!
///
@@ -137,9 +137,12 @@ pub fn map(effect: Effect(a), f: fn(a) -> b) -> Effect(b) {
/// This is primarily used internally by the server component runtime, but it is
/// may also useful for testing.
///
-/// **Note:** You should not consider this function a part of the public API. It
-/// may be removed in a future minor or patch release.
-///
+/// **Note:** For now, you should **not** consider this function a part of the
+/// public API. It may be removed in a future minor or patch release. If you have
+/// a specific use case for this function, we'd love to hear about it! Please
+/// reach out on the [Gleam Discord](https://discord.gg/Fm8Pwmy) or
+/// [open an issue](https://github.com/lustre-labs/lustre/issues/new)!
+///
pub fn perform(
effect: Effect(a),
dispatch: fn(a) -> Nil,
diff --git a/src/lustre/element.gleam b/src/lustre/element.gleam
index 1f4d885..fc92ccd 100644
--- a/src/lustre/element.gleam
+++ b/src/lustre/element.gleam
@@ -1,5 +1,10 @@
//// Lustre wouldn't be much use as a frontend framework if it didn't provide a
-//// way to create HTML elements. This module contains
+//// way to create HTML elements. This module contains the basic functions
+//// necessary to construct and manipulate different HTML elements.
+////
+//// It is also possible to use Lustre as a HTML templating library, without
+//// using its runtime or framework features.
+////
// IMPORTS ---------------------------------------------------------------------
@@ -14,49 +19,52 @@ import lustre/internals/vdom.{Element, Text}
/// The `Element` type is how Lustre represents chunks of HTML. The `msg` type
/// variable is used to represent the types of messages that can be produced from
/// events on the element or its children.
-///
+///
/// **Note:** Just because an element _can_ produces messages of a given type,
/// doesn't mean that it _will_! The `msg` type variable is used to represent the
/// potential for messages to be produced, not a guarantee.
-///
+///
/// The most basic ways to create elements are:
-///
+///
/// - The [`element`](#element) function to construct arbitrary HTML elements.
/// You can also use this render Custom Elements (like those registered as
/// Lustre components).
-///
+///
/// - The [`text`](#text) function to turn a Gleam string into a text node.
-///
+///
/// - The [`none`](#none) function to render nothing - useful for conditional
/// rendering.
-///
+///
/// If you have more complex needs, there are two more-advanced functions:
-///
+///
/// - The [`namespaced`](#namespaced) function to create elements in a specific
/// XML namespace. This is useful for SVG or MathML elements, for example.
-///
+///
/// - The [`advanced`](#advanced) function to create elements with more control
/// over how the element is rendered when converted to a string. This is
/// necessary because some HTML, SVG, and MathML elements are self-closing or
/// void elements, and Lustre needs to know how to render them correctly!
-///
+///
/// For most applications, you'll only need to use the simpler functions; usually
/// the [`text`](#text) and [`none`](#none) functions are enough. This is because
/// Lustre already provides a module with all the standard HTML and SVG elements
/// ready to use in [`lustre/element/html`](./element/html) and
/// [`lustre/element/svg`](./element/svg).
-///
+///
pub type Element(msg) =
vdom.Element(msg)
// CONSTRUCTORS ----------------------------------------------------------------
-///
-///
+/// A general function for constructing any kind of element. In most cases you
+/// will want to use the [`lustre/element/html`](./element/html) instead but this
+/// function is particularly handing when constructing custom elements, either
+/// from your own Lustre components or from external JavaScript libraries.
+///
/// **Note:** Because Lustre is primarily used to create HTML, this function
/// spcieal-cases the following tags render as
/// [void elements](https://developer.mozilla.org/en-US/docs/Glossary/Void_element):
-///
+///
/// - area
/// - base
/// - br
@@ -71,12 +79,12 @@ pub type Element(msg) =
/// - source
/// - track
/// - wbr
-///
+///
/// This will only affect the output of `to_string` and `to_string_builder`!
/// If you need to render any of these tags with children, *or* you want to
/// render some other tag as self-closing or void, use [`advanced`](#advanced)
/// to construct the element instead.
-///
+///
pub fn element(
tag: String,
attrs: List(Attribute(msg)),
@@ -109,6 +117,8 @@ pub fn element(
}
}
+/// A function for constructing elements in a specific XML namespace. This can
+/// be used to construct SVG or MathML elements, for example.
///
pub fn namespaced(
namespace: String,
@@ -126,6 +136,10 @@ pub fn namespaced(
)
}
+/// A function for constructing elements with more control over how the element
+/// is rendered when converted to a string. This is necessary because some HTML,
+/// SVG, and MathML elements are self-closing or void elements, and Lustre needs
+/// to know how to render them correctly!
///
pub fn advanced(
namespace: String,
@@ -138,11 +152,18 @@ pub fn advanced(
Element(tag, namespace, attrs, children, self_closing, void)
}
-///
+/// A function for turning a Gleam string into a text node. Gleam doesn't have
+/// union types like some other languages you may be familiar with, like TypeScript.
+/// Instead, we need a way to take a `String` and turn it into an `Element` somehow:
+/// this function is exactly that!
+///
pub fn text(content: String) -> Element(msg) {
Text(content)
}
+/// A function for rendering nothing. This is mostly useful for conditional
+/// rendering, where you might want to render something only if a certain
+/// condition is met.
///
pub fn none() -> Element(msg) {
Text("")
@@ -169,9 +190,9 @@ fn escape(escaped: String, content: String) -> String {
/// from events. Sometimes you might end up with a fragment of HTML from another
/// library or module that produces a different type of message: this function lets
/// you map the messages produced from one type to another.
-///
+///
/// Think of it like `list.map` or `result.map` but for HTML events!
-///
+///
pub fn map(element: Element(a), f: fn(a) -> b) -> Element(b) {
case element {
Text(content) -> Text(content)
@@ -189,11 +210,22 @@ pub fn map(element: Element(a), f: fn(a) -> b) -> Element(b) {
// CONVERSIONS -----------------------------------------------------------------
-///
+/// Convert a Lustre `Element` to a string. This is _not_ pretty-printed, so
+/// there are no newlines or indentation. If you need to pretty-print an element,
+/// reach out on the [Gleam Discord](https://discord.gg/Fm8Pwmy) or
+/// [open an issue](https://github.com/lustre-labs/lustre/issues/new) with your
+/// use case and we'll see what we can do!
+///
pub fn to_string(element: Element(msg)) -> String {
vdom.element_to_string(element)
}
+/// Convert a Lustre `Element` to a `StringBuilder`. This is _not_ pretty-printed,
+/// so there are no newlines or indentation. If you need to pretty-print an element,
+/// reach out on the [Gleam Discord](https://discord.gg/Fm8Pwmy) or
+/// [open an issue](https://github.com/lustre-labs/lustre/issues/new) with your
+/// use case and we'll see what we can do!
+///
pub fn to_string_builder(element: Element(msg)) -> StringBuilder {
vdom.element_to_string_builder(element)
}
diff --git a/src/lustre/element/html.gleam b/src/lustre/element/html.gleam
index 248867d..426c65b 100644
--- a/src/lustre/element/html.gleam
+++ b/src/lustre/element/html.gleam
@@ -1,14 +1,11 @@
-//// To read the full documentation for this module, please visit
-//// [https://lustre.build/api/lustre/element/html](https://lustre.build/api/lustre/element/html)
-
// IMPORTS ---------------------------------------------------------------------
-import lustre/element.{type Element, element, namespaced, text}
+import lustre/element.{type Element, element, namespaced}
import lustre/attribute.{type Attribute}
// HTML ELEMENTS: MAIN ROOT ----------------------------------------------------
-///
+///
pub fn html(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -16,14 +13,18 @@ pub fn html(
element("html", attrs, children)
}
+pub fn text(content: String) -> Element(msg) {
+ element.text(content)
+}
+
// HTML ELEMENTS: DOCUMENT METADATA --------------------------------------------
-///
+///
pub fn base(attrs: List(Attribute(msg))) -> Element(msg) {
element("base", attrs, [])
}
-///
+///
pub fn head(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -31,29 +32,29 @@ pub fn head(
element("head", attrs, children)
}
-///
+///
pub fn link(attrs: List(Attribute(msg))) -> Element(msg) {
element("link", attrs, [])
}
-///
+///
pub fn meta(attrs: List(Attribute(msg))) -> Element(msg) {
element("meta", attrs, [])
}
-///
+///
pub fn style(attrs: List(Attribute(msg)), css: String) -> Element(msg) {
element("style", attrs, [text(css)])
}
-///
+///
pub fn title(attrs: List(Attribute(msg)), content: String) -> Element(msg) {
element("title", attrs, [text(content)])
}
// HTML ELEMENTS: SECTIONING ROOT -----------------------------------------------
-///
+///
pub fn body(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -63,7 +64,7 @@ pub fn body(
// HTML ELEMENTS: CONTENT SECTIONING -------------------------------------------
-///
+///
pub fn address(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -71,7 +72,7 @@ pub fn address(
element("address", attrs, children)
}
-///
+///
pub fn article(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -79,7 +80,7 @@ pub fn article(
element("article", attrs, children)
}
-///
+///
pub fn aside(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -87,7 +88,7 @@ pub fn aside(
element("aside", attrs, children)
}
-///
+///
pub fn footer(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -95,7 +96,7 @@ pub fn footer(
element("footer", attrs, children)
}
-///
+///
pub fn header(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -103,7 +104,7 @@ pub fn header(
element("header", attrs, children)
}
-///
+///
pub fn h1(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -111,7 +112,7 @@ pub fn h1(
element("h1", attrs, children)
}
-///
+///
pub fn h2(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -119,7 +120,7 @@ pub fn h2(
element("h2", attrs, children)
}
-///
+///
pub fn h3(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -127,7 +128,7 @@ pub fn h3(
element("h3", attrs, children)
}
-///
+///
pub fn h4(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -135,7 +136,7 @@ pub fn h4(
element("h4", attrs, children)
}
-///
+///
pub fn h5(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -143,7 +144,7 @@ pub fn h5(
element("h5", attrs, children)
}
-///
+///
pub fn h6(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -151,7 +152,7 @@ pub fn h6(
element("h6", attrs, children)
}
-///
+///
pub fn hgroup(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -159,7 +160,7 @@ pub fn hgroup(
element("hgroup", attrs, children)
}
-///
+///
pub fn main(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -167,7 +168,7 @@ pub fn main(
element("main", attrs, children)
}
-///
+///
pub fn nav(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -175,7 +176,7 @@ pub fn nav(
element("nav", attrs, children)
}
-///
+///
pub fn section(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -183,7 +184,7 @@ pub fn section(
element("section", attrs, children)
}
-///
+///
pub fn search(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -193,7 +194,7 @@ pub fn search(
// HTML ELEMENTS: TEXT CONTENT -------------------------------------------------
-///
+///
pub fn blockquote(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -201,7 +202,7 @@ pub fn blockquote(
element("blockquote", attrs, children)
}
-///
+///
pub fn dd(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -209,7 +210,7 @@ pub fn dd(
element("dd", attrs, children)
}
-///
+///
pub fn div(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -217,7 +218,7 @@ pub fn div(
element("div", attrs, children)
}
-///
+///
pub fn dl(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -225,7 +226,7 @@ pub fn dl(
element("dl", attrs, children)
}
-///
+///
pub fn dt(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -233,7 +234,7 @@ pub fn dt(
element("dt", attrs, children)
}
-///
+///
pub fn figcaption(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -241,7 +242,7 @@ pub fn figcaption(
element("figcaption", attrs, children)
}
-///
+///
pub fn figure(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -249,12 +250,12 @@ pub fn figure(
element("figure", attrs, children)
}
-///
+///
pub fn hr(attrs: List(Attribute(msg))) -> Element(msg) {
element("hr", attrs, [])
}
-///
+///
pub fn li(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -262,7 +263,7 @@ pub fn li(
element("li", attrs, children)
}
-///
+///
pub fn menu(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -278,7 +279,7 @@ pub fn ol(
element("ol", attrs, children)
}
-///
+///
pub fn p(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -286,7 +287,7 @@ pub fn p(
element("p", attrs, children)
}
-///
+///
pub fn pre(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -294,7 +295,7 @@ pub fn pre(
element("pre", attrs, children)
}
-///
+///
pub fn ul(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -304,7 +305,7 @@ pub fn ul(
// HTML ELEMENTS: INLINE TEXT SEMANTICS ----------------------------------------
-///
+///
pub fn a(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -312,7 +313,7 @@ pub fn a(
element("a", attrs, children)
}
-///
+///
pub fn abbr(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -320,7 +321,7 @@ pub fn abbr(
element("abbr", attrs, children)
}
-///
+///
pub fn b(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -328,7 +329,7 @@ pub fn b(
element("b", attrs, children)
}
-///
+///
pub fn bdi(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -336,7 +337,7 @@ pub fn bdi(
element("bdi", attrs, children)
}
-///
+///
pub fn bdo(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -344,12 +345,12 @@ pub fn bdo(
element("bdo", attrs, children)
}
-///
+///
pub fn br(attrs: List(Attribute(msg))) -> Element(msg) {
element("br", attrs, [])
}
-///
+///
pub fn cite(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -357,7 +358,7 @@ pub fn cite(
element("cite", attrs, children)
}
-///
+///
pub fn code(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -365,7 +366,7 @@ pub fn code(
element("code", attrs, children)
}
-///
+///
pub fn data(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -373,7 +374,7 @@ pub fn data(
element("data", attrs, children)
}
-///
+///
pub fn dfn(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -381,7 +382,7 @@ pub fn dfn(
element("dfn", attrs, children)
}
-///
+///
pub fn em(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -389,7 +390,7 @@ pub fn em(
element("em", attrs, children)
}
-///
+///
pub fn i(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -397,7 +398,7 @@ pub fn i(
element("i", attrs, children)
}
-///
+///
pub fn kbd(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -405,7 +406,7 @@ pub fn kbd(
element("kbd", attrs, children)
}
-///
+///
pub fn mark(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -413,7 +414,7 @@ pub fn mark(
element("mark", attrs, children)
}
-///
+///
pub fn q(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -421,7 +422,7 @@ pub fn q(
element("q", attrs, children)
}
-///
+///
pub fn rp(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -429,7 +430,7 @@ pub fn rp(
element("rp", attrs, children)
}
-///
+///
pub fn rt(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -437,7 +438,7 @@ pub fn rt(
element("rt", attrs, children)
}
-///
+///
pub fn ruby(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -445,7 +446,7 @@ pub fn ruby(
element("ruby", attrs, children)
}
-///
+///
pub fn s(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -453,7 +454,7 @@ pub fn s(
element("s", attrs, children)
}
-///
+///
pub fn samp(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -461,7 +462,7 @@ pub fn samp(
element("samp", attrs, children)
}
-///
+///
pub fn small(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -469,7 +470,7 @@ pub fn small(
element("small", attrs, children)
}
-///
+///
pub fn span(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -477,7 +478,7 @@ pub fn span(
element("span", attrs, children)
}
-///
+///
pub fn strong(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -485,7 +486,7 @@ pub fn strong(
element("strong", attrs, children)
}
-///
+///
pub fn sub(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -493,7 +494,7 @@ pub fn sub(
element("sub", attrs, children)
}
-///
+///
pub fn sup(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -501,7 +502,7 @@ pub fn sup(
element("sup", attrs, children)
}
-///
+///
pub fn time(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -509,7 +510,7 @@ pub fn time(
element("time", attrs, children)
}
-///
+///
pub fn u(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -517,7 +518,7 @@ pub fn u(
element("u", attrs, children)
}
-///
+///
pub fn var(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -525,19 +526,19 @@ pub fn var(
element("var", attrs, children)
}
-///
+///
pub fn wbr(attrs: List(Attribute(msg))) -> Element(msg) {
element("wbr", attrs, [])
}
// HTML ELEMENTS: IMAGE AND MULTIMEDIA -----------------------------------------
-///
+///
pub fn area(attrs: List(Attribute(msg))) -> Element(msg) {
element("area", attrs, [])
}
-///
+///
pub fn audio(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -545,13 +546,13 @@ pub fn audio(
element("audio", attrs, children)
}
-///
+///
pub fn img(attrs: List(Attribute(msg))) -> Element(msg) {
element("img", attrs, [])
}
/// Used with <area> elements to define an image map (a clickable link area).
-///
+///
pub fn map(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -559,12 +560,12 @@ pub fn map(
element("map", attrs, children)
}
-///
+///
pub fn track(attrs: List(Attribute(msg))) -> Element(msg) {
element("track", attrs, [])
}
-///
+///
pub fn video(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -574,22 +575,22 @@ pub fn video(
// HTML ELEMENTS: EMBEDDED CONTENT ---------------------------------------------
-///
+///
pub fn embed(attrs: List(Attribute(msg))) -> Element(msg) {
element("embed", attrs, [])
}
-///
+///
pub fn iframe(attrs: List(Attribute(msg))) -> Element(msg) {
element("iframe", attrs, [])
}
-///
+///
pub fn object(attrs: List(Attribute(msg))) -> Element(msg) {
element("object", attrs, [])
}
-///
+///
pub fn picture(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -597,19 +598,19 @@ pub fn picture(
element("picture", attrs, children)
}
-///
+///
pub fn portal(attrs: List(Attribute(msg))) -> Element(msg) {
element("portal", attrs, [])
}
-///
+///
pub fn source(attrs: List(Attribute(msg))) -> Element(msg) {
element("source", attrs, [])
}
// HTML ELEMENTS: SVG AND MATHML -----------------------------------------------
-///
+///
pub fn svg(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -617,7 +618,7 @@ pub fn svg(
namespaced("http://www.w3.org/2000/svg", "svg", attrs, children)
}
-///
+///
pub fn math(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -627,12 +628,12 @@ pub fn math(
// HTML ELEMENTS: SCRIPTING ----------------------------------------------------
-///
+///
pub fn canvas(attrs: List(Attribute(msg))) -> Element(msg) {
element("canvas", attrs, [])
}
-///
+///
pub fn noscript(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -640,14 +641,14 @@ pub fn noscript(
element("noscript", attrs, children)
}
-///
+///
pub fn script(attrs: List(Attribute(msg)), js: String) -> Element(msg) {
element("script", attrs, [text(js)])
}
// HTML ELEMENTS: DEMARCATING EDITS ---------------------------------------------
-///
+///
pub fn del(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -655,7 +656,7 @@ pub fn del(
element.element("del", attrs, children)
}
-///
+///
pub fn ins(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -665,7 +666,7 @@ pub fn ins(
// HTML ELEMENTS: TABLE CONTENT ------------------------------------------------
-///
+///
pub fn caption(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -673,12 +674,12 @@ pub fn caption(
element.element("caption", attrs, children)
}
-///
+///
pub fn col(attrs: List(Attribute(msg))) -> Element(msg) {
element.element("col", attrs, [])
}
-///
+///
pub fn colgroup(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -686,7 +687,7 @@ pub fn colgroup(
element.element("colgroup", attrs, children)
}
-///
+///
pub fn table(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -694,7 +695,7 @@ pub fn table(
element.element("table", attrs, children)
}
-///
+///
pub fn tbody(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -702,7 +703,7 @@ pub fn tbody(
element.element("tbody", attrs, children)
}
-///
+///
pub fn td(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -710,7 +711,7 @@ pub fn td(
element.element("td", attrs, children)
}
-///
+///
pub fn tfoot(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -718,7 +719,7 @@ pub fn tfoot(
element.element("tfoot", attrs, children)
}
-///
+///
pub fn th(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -726,7 +727,7 @@ pub fn th(
element.element("th", attrs, children)
}
-///
+///
pub fn thead(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -734,7 +735,7 @@ pub fn thead(
element.element("thead", attrs, children)
}
-///
+///
pub fn tr(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -744,7 +745,7 @@ pub fn tr(
// HTML ELEMENTS: FORMS --------------------------------------------------------
-///
+///
pub fn button(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -752,7 +753,7 @@ pub fn button(
element.element("button", attrs, children)
}
-///
+///
pub fn datalist(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -760,7 +761,7 @@ pub fn datalist(
element.element("datalist", attrs, children)
}
-///
+///
pub fn fieldset(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -768,7 +769,7 @@ pub fn fieldset(
element.element("fieldset", attrs, children)
}
-///
+///
pub fn form(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -776,12 +777,12 @@ pub fn form(
element.element("form", attrs, children)
}
-///
+///
pub fn input(attrs: List(Attribute(msg))) -> Element(msg) {
element.element("input", attrs, [])
}
-///
+///
pub fn label(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -789,7 +790,7 @@ pub fn label(
element.element("label", attrs, children)
}
-///
+///
pub fn legend(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -797,7 +798,7 @@ pub fn legend(
element.element("legend", attrs, children)
}
-///
+///
pub fn meter(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -805,7 +806,7 @@ pub fn meter(
element.element("meter", attrs, children)
}
-///
+///
pub fn optgroup(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -813,12 +814,12 @@ pub fn optgroup(
element.element("optgroup", attrs, children)
}
-///
+///
pub fn option(attrs: List(Attribute(msg))) -> Element(msg) {
element.element("option", attrs, [])
}
-///
+///
pub fn output(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -826,7 +827,7 @@ pub fn output(
element.element("output", attrs, children)
}
-///
+///
pub fn progress(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -834,7 +835,7 @@ pub fn progress(
element.element("progress", attrs, children)
}
-///
+///
pub fn select(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -842,14 +843,14 @@ pub fn select(
element.element("select", attrs, children)
}
-///
+///
pub fn textarea(attrs: List(Attribute(msg))) -> Element(msg) {
element.element("textarea", attrs, [])
}
// HTML ELEMENTS: INTERACTIVE ELEMENTS -----------------------------------------
-///
+///
pub fn details(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -857,7 +858,7 @@ pub fn details(
element.element("details", attrs, children)
}
-///
+///
pub fn dialog(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -865,7 +866,7 @@ pub fn dialog(
element.element("dialog", attrs, children)
}
-///
+///
pub fn summary(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
@@ -880,7 +881,7 @@ pub fn slot(attrs: List(Attribute(msg))) -> Element(msg) {
element.element("slot", attrs, [])
}
-///
+///
pub fn template(
attrs: List(Attribute(msg)),
children: List(Element(msg)),
diff --git a/src/lustre/element/svg.gleam b/src/lustre/element/svg.gleam
index 447a4c6..1979c2a 100644
--- a/src/lustre/element/svg.gleam
+++ b/src/lustre/element/svg.gleam
@@ -1,6 +1,3 @@
-//// To read the full documentation for this module, please visit
-//// [https://lustre.build/api/lustre/element/svg](https://lustre.build/api/lustre/element/svg)
-
// IMPORTS ---------------------------------------------------------------------
import lustre/element.{type Element, namespaced, text as inline_text}
diff --git a/src/lustre/event.gleam b/src/lustre/event.gleam
index 64628b0..1658504 100644
--- a/src/lustre/event.gleam
+++ b/src/lustre/event.gleam
@@ -1,6 +1,3 @@
-//// To read the full documentation for this module, please visit
-//// [https://lustre.build/api/lustre/event](https://lustre.build/api/lustre/event)
-
// IMPORTS ---------------------------------------------------------------------
import gleam/dynamic.{type DecodeError, type Dynamic}
@@ -16,6 +13,11 @@ type Decoded(a) =
// EFFECTS ---------------------------------------------------------------------
+/// Dispatches a custom message from a Lustre component. This lets components
+/// communicate with their parents the same way native DOM elements do.
+///
+/// Any JSON-serialisable payload can be attached as additional data for any
+/// event listeners to decode. This data will be on the event's `detail` property.
///
pub fn emit(event: String, data: Json) -> Effect(msg) {
effect.event(event, data)
@@ -23,6 +25,13 @@ pub fn emit(event: String, data: Json) -> Effect(msg) {
// CUSTOM EVENTS ---------------------------------------------------------------
+/// Listens for the given event and applies the handler to the event object. If
+/// the handler returns an `Ok` the resulting message will be dispatched, otherwise
+/// the event (and any decoding error) will be ignored.
+///
+/// The event name is typically an all-lowercase string such as "click" or "mousemove".
+/// If you're listening for non-standard events (like those emitted by a custom
+/// element) their event names might be slightly different.
///
pub fn on(
name: String,
@@ -127,6 +136,10 @@ pub fn on_check(msg: fn(Bool) -> msg) -> Attribute(msg) {
|> result.map(msg)
}
+/// Listens for the form's `submit` event, and dispatches the given message. This
+/// will automatically call [`prevent_default`](#prevent_default) to stop the form
+/// from submitting.
+///
pub fn on_submit(msg: msg) -> Attribute(msg) {
use event <- on("submit")
let _ = prevent_default(event)
@@ -148,18 +161,26 @@ pub fn on_blur(msg: msg) -> Attribute(msg) {
// DECODERS --------------------------------------------------------------------
+/// Decoding an input element's `value` is such a common operation that we have
+/// a dedicated decoder for it. This attempts to decoder `event.target.value` as
+/// a string.
///
pub fn value(event: Dynamic) -> Decoded(String) {
event
|> dynamic.field("target", dynamic.field("value", dynamic.string))
}
+/// Similar to [`value`](#value), decoding a checkbox's `checked` state is common
+/// enough to warrant a dedicated decoder. This attempts to decode
+/// `event.target.checked` as a boolean.
///
pub fn checked(event: Dynamic) -> Decoded(Bool) {
event
|> dynamic.field("target", dynamic.field("checked", dynamic.bool))
}
+/// Decodes the mouse position from any event that has a `clientX` and `clientY`
+/// property.
///
pub fn mouse_position(event: Dynamic) -> Decoded(#(Float, Float)) {
use x <- result.then(dynamic.field("clientX", dynamic.float)(event))
@@ -170,11 +191,29 @@ pub fn mouse_position(event: Dynamic) -> Decoded(#(Float, Float)) {
// UTILS -----------------------------------------------------------------------
+/// Calls an event's `preventDefault` method. If the `Dynamic` does not have a
+/// `preventDefault` method, this function does nothing.
+///
+/// As the name implies, `preventDefault` will prevent any default action associated
+/// with an event from occuring. For example, if you call `preventDefault` on a
+/// `submit` event, the form will not be submitted.
+///
+/// See: https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault
+///
@external(javascript, "../client-runtime.ffi.mjs", "prevent_default")
pub fn prevent_default(_event: Dynamic) -> Nil {
Nil
}
+/// Calls an event's `stopPropagation` method. If the `Dynamic` does not have a
+/// `stopPropagation` method, this function does nothing.
+///
+/// Stopping event propagation means the event will not "bubble" up to parent
+/// elements. If any elements higher up in the DOM have event listeners for the
+/// same event, they will not be called.
+///
+/// See: https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation
+///
@external(javascript, "../client-runtime.ffi.mjs", "stop_propagation")
pub fn stop_propagation(_event: Dynamic) -> Nil {
Nil
diff --git a/src/lustre/internals/runtime.gleam b/src/lustre/internals/runtime.gleam
index b0e264f..716daac 100644
--- a/src/lustre/internals/runtime.gleam
+++ b/src/lustre/internals/runtime.gleam
@@ -219,42 +219,3 @@ fn run_effects(effects: Effect(msg), self: Subject(Action(msg, runtime))) -> Nil
effect.perform(effects, dispatch, emit)
}
-
-// Empty implementations of every function in this module are required because we
-// need to be able to build the codebase *locally* with the JavaScript target to
-// bundle the server component runtime.
-//
-// For *consumers* of Lustre this is not a problem, Gleam will see this module is
-// never included in any path reachable from JavaScript but when we're *inside the
-// package* Gleam has no idea that is the case.
-
-@target(javascript)
-pub fn start(
- init: #(model, Effect(msg)),
- update: fn(model, msg) -> #(model, Effect(msg)),
- view: fn(model) -> Element(msg),
- on_attribute_change: Dict(String, Decoder(msg)),
-) -> Result(Subject(Action(msg, runtime)), StartError) {
- panic
-}
-
-@target(javascript)
-fn loop(
- message: Action(msg, runtime),
- state: State(model, msg, runtime),
-) -> Next(Action(msg, runtime), State(model, msg, runtime)) {
- panic
-}
-
-@target(javascript)
-fn run_renderers(
- renderers: Dict(any, fn(Patch(msg)) -> Nil),
- patch: Patch(msg),
-) -> Nil {
- panic
-}
-
-@target(javascript)
-fn run_effects(effects: Effect(msg), self: Subject(Action(msg, runtime))) -> Nil {
- panic
-}