aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHayleigh Thompson <me@hayleigh.dev>2023-08-19 22:21:19 +0100
committerHayleigh Thompson <me@hayleigh.dev>2023-08-19 22:21:19 +0100
commit9919bc2702c89168d1805eaa0db9e4baff091260 (patch)
treeef5f1fd360d90ec8433aa0fccc2709bed0e8e9d2 /src
parente4aa0e04e54105395d5f6f5e3f7e2d9a4f7851e0 (diff)
downloadlustre-9919bc2702c89168d1805eaa0db9e4baff091260.tar.gz
lustre-9919bc2702c89168d1805eaa0db9e4baff091260.zip
:truck: Shift things around to accomodate a monorepo.
Diffstat (limited to 'src')
-rw-r--r--src/lustre.ffi.mjs206
-rw-r--r--src/lustre.gleam254
-rw-r--r--src/lustre/attribute.gleam408
-rw-r--r--src/lustre/effect.gleam67
-rw-r--r--src/lustre/element.gleam126
-rw-r--r--src/lustre/element/html.gleam1197
-rw-r--r--src/lustre/element/svg.gleam351
-rw-r--r--src/lustre/event.gleam184
-rw-r--r--src/runtime.ffi.mjs230
9 files changed, 0 insertions, 3023 deletions
diff --git a/src/lustre.ffi.mjs b/src/lustre.ffi.mjs
deleted file mode 100644
index b99f6e2..0000000
--- a/src/lustre.ffi.mjs
+++ /dev/null
@@ -1,206 +0,0 @@
-import { ElementNotFound, ComponentAlreadyRegistered } from "./lustre.mjs";
-import { from } from "./lustre/effect.mjs";
-import { map } from "./lustre/element.mjs";
-import { morph } from "./runtime.ffi.mjs";
-import { Ok, Error, isEqual } from "./gleam.mjs";
-
-// RUNTIME ---------------------------------------------------------------------
-
-///
-///
-export class App {
- #root = null;
- #state = null;
- #queue = [];
- #effects = [];
- #didUpdate = false;
-
- #init = null;
- #update = null;
- #view = null;
-
- constructor(init, update, render) {
- this.#init = init;
- this.#update = update;
- this.#view = render;
- }
-
- start(selector = "body") {
- if (this.#root) return this;
-
- try {
- const el =
- selector instanceof HTMLElement
- ? selector
- : document.querySelector(selector);
- const [next, effects] = this.#init();
-
- this.#root = el;
- this.#state = next;
- this.#effects = effects[0].toArray();
- this.#didUpdate = true;
-
- window.requestAnimationFrame(() => this.#tick());
-
- return new Ok((msg) => this.dispatch(msg));
- } catch (_) {
- return new Error(new ElementNotFound());
- }
- }
-
- dispatch(msg) {
- this.#queue.push(msg);
- this.#tick();
- }
-
- emit(name, event = null) {
- this.#root.dispatchEvent(
- new CustomEvent(name, {
- bubbles: true,
- detail: event,
- composed: true,
- })
- );
- }
-
- destroy() {
- this.#root.remove();
- this.#state = null;
- this.#queue = [];
- this.#effects = [];
- this.#didUpdate = false;
- this.#update = () => {};
- this.#view = () => {};
- }
-
- #render() {
- const node = this.#view(this.#state);
- const vdom = map(node, (msg) => this.dispatch(msg));
-
- morph(this.#root, vdom);
- }
-
- #tick() {
- this.#flush();
- this.#didUpdate && this.#render();
- this.#didUpdate = false;
- }
-
- #flush(times = 0) {
- if (this.#queue.length) {
- while (this.#queue.length) {
- const [next, effects] = this.#update(this.#state, this.#queue.shift());
-
- this.#state = next;
- this.#effects = this.#effects.concat(effects[0].toArray());
- }
- this.#didUpdate = true;
- }
-
- // Each update can produce effects which must now be executed.
- while (this.#effects[0])
- this.#effects.shift()(
- (msg) => this.dispatch(msg),
- (name, data) => this.emit(name, data)
- );
-
- // Synchronous effects will immediately queue a message to be processed. If
- // it is reasonable, we can process those updates too before proceeding to
- // the next render.
- if (this.#queue.length) {
- times >= 5 ? console.warn(tooManyUpdates) : this.#flush(++times);
- }
- }
-}
-
-export const setup = (init, update, render) => new App(init, update, render);
-export const start = (app, selector) => app.start(selector);
-
-export const emit = (name, data) =>
- // Normal `Effect`s constructed in Gleam from `effect.from` don't get told
- // about the second argument, but it's there 👀.
- from((_, emit) => {
- emit(name, data);
- });
-
-// HTML EVENTS -----------------------------------------------------------------
-
-export const prevent_default = (e) => e.preventDefault?.();
-export const stop_propagation = (e) => e.stopPropagation?.();
-
-// CUSTOM ELEMENTS -------------------------------------------------------------
-
-export const setup_component = (
- name,
- init,
- update,
- render,
- on_attribute_change
-) => {
- if (customElements.get(name)) {
- return new Error(new ComponentAlreadyRegistered());
- }
-
- customElements.define(
- name,
- class extends HTMLElement {
- static get observedAttributes() {
- return on_attribute_change.entries().map(([name, _]) => name);
- }
-
- #container = document.createElement("div");
- #app = null;
- #dispatch = null;
-
- constructor() {
- super();
-
- this.#app = new App(init, update, render);
- const dispatch = this.#app.start(this.#container);
- this.#dispatch = dispatch[0];
-
- on_attribute_change.forEach((decoder, name) => {
- Object.defineProperty(this, name, {
- get: () => {
- return this[`_${name}`] || this.getAttribute(name);
- },
-
- set: (value) => {
- const prev = this[name];
- const decoded = decoder(value);
-
- // We need this equality check to prevent constantly dispatching
- // messages when the value is an object or array: it might not have
- // changed but its reference might have and we don't want to trigger
- // useless updates.
- if (decoded.isOk() && !isEqual(prev, decoded[0])) {
- this.#dispatch(decoded[0]);
- }
-
- if (typeof value === "string") {
- this.setAttribute(name, value);
- } else {
- this[`_${name}`] = value;
- }
- },
- });
- });
- }
-
- connectedCallback() {
- this.appendChild(this.#container);
- }
-
- attributeChangedCallback(name, prev, next) {
- if (prev !== next) {
- this[name] = next;
- }
- }
-
- disconnectedCallback() {
- this.#app.destroy();
- }
- }
- );
- return new Ok(null);
-};
diff --git a/src/lustre.gleam b/src/lustre.gleam
deleted file mode 100644
index 673f982..0000000
--- a/src/lustre.gleam
+++ /dev/null
@@ -1,254 +0,0 @@
-//// Lustre is a declarative framework for building Web apps in Gleam.
-
-// IMPORTS ---------------------------------------------------------------------
-
-import gleam/dynamic.{Decoder}
-import gleam/map.{Map}
-import lustre/effect.{Effect}
-import lustre/element.{Element}
-
-// TYPES -----------------------------------------------------------------------
-
-/// An `App` describes a Lustre application: what state it holds and what kind
-/// 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:
-/// [`basic`](#basic) and [`application`](#application). Although you can't do
-/// anything but [`start`](#start) them, the constructors are separated in case
-/// you want to set up an application but defer starting it until some later point
-/// in time.
-///
-/// ```text
-/// +--------+
-/// | |
-/// | update |
-/// | |
-/// +--------+
-/// ^ |
-/// | |
-/// Msg | | #(Model, Effect(Msg))
-/// | |
-/// | v
-/// +------+ +------------------------+
-/// | | #(Model, Effect(Msg)) | |
-/// | init |------------------------>| Lustre Runtime |
-/// | | | |
-/// +------+ +------------------------+
-/// ^ |
-/// | |
-/// Msg | | Model
-/// | |
-/// | v
-/// +--------+
-/// | |
-/// | render |
-/// | |
-/// +--------+
-/// ```
-///
-pub type App(model, msg)
-
-pub type Error {
- ElementNotFound
- ComponentAlreadyRegistered
-}
-
-// These types aren't exposed, but they're just here to try and shrink the type
-// annotations for `App` and `application` a little bit. When generating docs,
-// Gleam automatically expands type aliases so this is purely for the benefit of
-// those reading the source.
-//
-
-type Update(model, msg) =
- fn(model, msg) -> #(model, Effect(msg))
-
-type Render(model, msg) =
- fn(model) -> Element(msg)
-
-// CONSTRUCTORS ----------------------------------------------------------------
-
-@target(javascript)
-/// Create a basic lustre app that just renders some element on the page.
-/// Note that this doesn't mean the content is static! With `element.stateful`
-/// you can still create components with local state.
-///
-/// Basic lustre apps don't have any *global* application state and so the
-/// plumbing is a lot simpler. If you find yourself passing lots of state around,
-/// 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!")
-/// ])
-/// )
-///
-/// assert Ok(_) = lustre.start(app, "#root")
-/// }
-/// ```
-///
-pub fn element(element: Element(msg)) -> App(Nil, msg) {
- let init = fn() { #(Nil, effect.none()) }
- let update = fn(_, _) { #(Nil, effect.none()) }
- let render = fn(_) { element }
-
- application(init, update, render)
-}
-
-@target(javascript)
-/// If you start off with a simple `[element`](#element) app, you may find
-/// yourself leaning on [`stateful`](./lustrel/element.html#stateful) elements
-/// 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 model.
-///
-/// ```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: fn() -> model,
- update: fn(model, msg) -> model,
- render: fn(model) -> Element(msg),
-) -> App(model, msg) {
- let init = fn() { #(init(), effect.none()) }
- let update = fn(model, msg) { #(update(model, msg), effect.none()) }
-
- application(init, update, render)
-}
-
-@target(javascript)
-/// An evolution of a [`simple`](#simple) app that allows you to return a
-/// [`Effect`](./lustre/effect.html#Effect) from your `init` and `update`s. Commands give
-/// us a way to perform side effects like sending an HTTP request or running a
-/// timer and then dispatch actions back to the runtime to trigger an `update`.
-///
-///```
-/// import lustre
-/// import lustre/effect
-/// 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 () -> Effect(Msg) {
-/// effect.from(fn (dispatch) {
-/// setInterval(fn () {
-/// dispatch(Tick)
-/// }, 1000)
-/// })
-/// }
-///
-/// external fn set_timeout (f: fn () -> a, delay: Int) -> Nil
-/// = "" "window.setTimeout"
-///```
-@external(javascript, "./lustre.ffi.mjs", "setup")
-pub fn application(init: fn() -> #(model, Effect(msg)), update: Update(
- model,
- msg,
- ), render: Render(model, msg)) -> App(model, msg)
-
-@target(javascript)
-@external(javascript, "./lustre.ffi.mjs", "setup_component")
-pub fn component(name: String, init: fn() -> #(model, Effect(msg)), update: Update(
- model,
- msg,
- ), render: Render(model, msg), on_attribute_change: Map(String, Decoder(msg))) -> Result(
- Nil,
- Error,
-)
-
-// EFFECTS ---------------------------------------------------------------------
-
-@target(javascript)
-/// Once you have created a app with either `basic` or `application`, you
-/// need to actually start it! This function will mount your app to the DOM
-/// node that matches the query selector you provide.
-///
-/// If everything mounted OK, we'll get back a dispatch function that you can
-/// 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")
-///
-/// dispatch(Incr)
-/// dispatch(Incr)
-/// dispatch(Incr)
-/// }
-///```
-///
-/// 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.
-///
-@external(javascript, "./lustre.ffi.mjs", "start")
-pub fn start(app: App(model, msg), selector: String) -> Result(
- fn(msg) -> Nil,
- Error,
-)
diff --git a/src/lustre/attribute.gleam b/src/lustre/attribute.gleam
deleted file mode 100644
index 459a86e..0000000
--- a/src/lustre/attribute.gleam
+++ /dev/null
@@ -1,408 +0,0 @@
-// IMPORTS ---------------------------------------------------------------------
-
-import gleam/dynamic.{Dynamic}
-import gleam/int
-import gleam/list
-import gleam/option.{Option}
-import gleam/string
-import gleam/string_builder.{StringBuilder}
-
-// TYPES -----------------------------------------------------------------------
-
-/// Attributes are attached to specific elements. They're either key/value pairs
-/// or event handlers.
-///
-pub opaque type Attribute(msg) {
- Attribute(String, Dynamic)
- Event(String, fn(Dynamic) -> Option(msg))
-}
-
-// CONSTRUCTORS ----------------------------------------------------------------
-
-///
-/// Lustre does some work internally to convert common Gleam values into ones that
-/// make sense for JavaScript. Here are the types that are converted:
-///
-/// - `List(a)` -> `Array(a)`
-/// - `Some(a)` -> `a`
-/// - `None` -> `undefined`
-///
-pub fn attribute(name: String, value: String) -> Attribute(msg) {
- escape("", value)
- |> dynamic.from
- |> Attribute(name, _)
-}
-
-///
-pub fn property(name: String, value: any) -> Attribute(msg) {
- Attribute(name, dynamic.from(value))
-}
-
-fn escape(escaped: String, content: String) -> String {
- case string.pop_grapheme(content) {
- Ok(#("<", xs)) -> escape(escaped <> "&lt;", xs)
- Ok(#(">", xs)) -> escape(escaped <> "&gt;", xs)
- Ok(#("&", xs)) -> escape(escaped <> "&amp;", xs)
- Ok(#("\"", xs)) -> escape(escaped <> "&quot;", xs)
- Ok(#("'", xs)) -> escape(escaped <> "&#x27;", xs)
- Ok(#(x, xs)) -> escape(escaped <> x, xs)
- Error(_) -> escaped <> content
- }
-}
-
-/// Attach custom event handlers to an element. A number of helper functions exist
-/// in this module to cover the most common events and use-cases, so you should
-/// check those out first.
-///
-/// If you need to handle an event that isn't covered by the helper functions,
-/// then you can use `on` to attach a custom event handler. The callback is given
-/// the event object as a `Dynamic`.
-///
-/// As a simple example, you can implement `on_click` like so:
-///
-/// ```gleam
-/// import gleam/option.{Some}
-/// import lustre/attribute.{Attribute}
-/// import lustre/event
-///
-/// pub fn on_click(msg: msg) -> Attribute(msg) {
-/// use _ <- event.on("click")
-/// Some(msg)
-/// }
-/// ```
-///
-/// By using `gleam/dynamic` you can decode the event object and pull out all sorts
-/// of useful data. This is how `on_input` is implemented:
-///
-/// ```gleam
-/// import gleam/dynamic
-/// import gleam/option.{None, Some}
-/// import gleam/result
-/// import lustre/attribute.{Attribute}
-/// import lustre/event
-///
-/// pub fn on_input(msg: fn(String) -> msg) -> Attribute(msg) {
-/// use event, dispatch <- on("input")
-/// let decode = dynamic.field("target", dynamic.field("value", dynamic.string))
-///
-/// case decode(event) {
-/// Ok(value) -> Some(msg(value))
-/// Error(_) -> None
-/// }
-/// }
-/// ```
-///
-/// You can take a look at the MDN reference for events
-/// [here](https://developer.mozilla.org/en-US/docs/Web/API/Event) to see what
-/// you can decode.
-///
-/// Unlike the helpers in the rest of this module, it is possible to simply ignore
-/// the dispatch function and not dispatch a message at all. In fact, we saw this
-/// with the `on_input` example above: if we can't decode the event object, we
-/// simply return `None` and emit nothing.
-///
-/// Beyond ignoring errors, this can be used to perform side effects we don't need
-/// to observe in our main application loop, such as logging...
-///
-/// ```gleam
-/// import gleam/io
-/// import gleam/option.{None}
-/// import lustre/attribute.{Attribute}
-/// import lustre/event
-///
-/// pub fn log_on_click(msg: String) -> Attribute(msg) {
-/// use _ <- event.on("click")
-/// io.println(msg)
-/// None
-/// }
-/// ```
-///
-pub fn on(name: String, handler: fn(Dynamic) -> Option(msg)) -> Attribute(msg) {
- Event("on" <> name, handler)
-}
-
-// MANIPULATIONS ---------------------------------------------------------------
-
-///
-///
-pub fn map(attr: Attribute(a), f: fn(a) -> b) -> Attribute(b) {
- case attr {
- Attribute(name, value) -> Attribute(name, value)
- Event(on, handler) -> Event(on, fn(e) { option.map(handler(e), f) })
- }
-}
-
-// CONVERSIONS -----------------------------------------------------------------
-
-///
-///
-pub fn to_string(attr: Attribute(msg)) -> String {
- case attr {
- Attribute(name, value) -> {
- case dynamic.classify(value) {
- "String" -> name <> "=\"" <> dynamic.unsafe_coerce(value) <> "\""
-
- // Boolean attributes are determined based on their presence, eg we don't
- // want to render `disabled="false"` if the value is `false` we simply
- // want to omit the attribute altogether.
- "Boolean" ->
- case dynamic.unsafe_coerce(value) {
- True -> name
- False -> ""
- }
-
- // For everything else we'll just make a best-effort serialisation.
- _ -> name <> "=\"" <> string.inspect(value) <> "\""
- }
- }
- Event(on, _) -> "data-lustre-on:" <> on
- }
-}
-
-///
-///
-pub fn to_string_builder(attr: Attribute(msg)) -> StringBuilder {
- case attr {
- Attribute(name, value) -> {
- case dynamic.classify(value) {
- "String" ->
- [name, "=\"", dynamic.unsafe_coerce(value), "\""]
- |> string_builder.from_strings
-
- // Boolean attributes are determined based on their presence, eg we don't
- // want to render `disabled="false"` if the value is `false` we simply
- // want to omit the attribute altogether.
- "Boolean" ->
- case dynamic.unsafe_coerce(value) {
- True -> string_builder.from_string(name)
- False -> string_builder.new()
- }
-
- // For everything else we'll just make a best-effort serialisation.
- _ ->
- [name, "=\"", string.inspect(value), "\""]
- |> string_builder.from_strings
- }
- }
- Event(on, _) ->
- ["data-lustre-on:", on]
- |> string_builder.from_strings
- }
-}
-
-// COMMON ATTRIBUTES -----------------------------------------------------------
-
-///
-pub fn style(properties: List(#(String, String))) -> Attribute(msg) {
- attribute(
- "style",
- {
- use styles, #(name, value) <- list.fold(properties, "")
- styles <> name <> ":" <> value <> ";"
- },
- )
-}
-
-///
-pub fn class(name: String) -> Attribute(msg) {
- attribute("class", name)
-}
-
-///
-pub fn classes(names: List(#(String, Bool))) -> Attribute(msg) {
- attribute(
- "class",
- names
- |> list.filter_map(fn(class) {
- case class.1 {
- True -> Ok(class.0)
- False -> Error(Nil)
- }
- })
- |> string.join(" "),
- )
-}
-
-///
-pub fn id(name: String) -> Attribute(msg) {
- attribute("id", name)
-}
-
-// INPUTS ----------------------------------------------------------------------
-
-///
-pub fn type_(name: String) -> Attribute(msg) {
- attribute("type", name)
-}
-
-///
-pub fn value(val: Dynamic) -> Attribute(msg) {
- property("value", val)
-}
-
-///
-pub fn checked(is_checked: Bool) -> Attribute(msg) {
- property("checked", is_checked)
-}
-
-///
-pub fn placeholder(text: String) -> Attribute(msg) {
- attribute("placeholder", text)
-}
-
-///
-pub fn selected(is_selected: Bool) -> Attribute(msg) {
- property("selected", is_selected)
-}
-
-// INPUT HELPERS ---------------------------------------------------------------
-
-///
-pub fn accept(types: List(String)) -> Attribute(msg) {
- attribute("accept", string.join(types, " "))
-}
-
-///
-pub fn accept_charset(types: List(String)) -> Attribute(msg) {
- attribute("acceptCharset", string.join(types, " "))
-}
-
-///
-pub fn msg(uri: String) -> Attribute(msg) {
- attribute("msg", uri)
-}
-
-///
-pub fn autocomplete(name: String) -> Attribute(msg) {
- attribute("autocomplete", name)
-}
-
-///
-pub fn autofocus(should_autofocus: Bool) -> Attribute(msg) {
- property("autoFocus", should_autofocus)
-}
-
-///
-pub fn disabled(is_disabled: Bool) -> Attribute(msg) {
- property("disabled", is_disabled)
-}
-
-///
-pub fn name(name: String) -> Attribute(msg) {
- attribute("name", name)
-}
-
-///
-pub fn pattern(regex: String) -> Attribute(msg) {
- attribute("pattern", regex)
-}
-
-///
-pub fn readonly(is_readonly: Bool) -> Attribute(msg) {
- property("readonly", is_readonly)
-}
-
-///
-pub fn required(is_required: Bool) -> Attribute(msg) {
- property("required", is_required)
-}
-
-///
-pub fn for(id: String) -> Attribute(msg) {
- attribute("for", id)
-}
-
-// INPUT RANGES ----------------------------------------------------------------
-
-///
-pub fn max(val: String) -> Attribute(msg) {
- attribute("max", val)
-}
-
-///
-pub fn min(val: String) -> Attribute(msg) {
- attribute("min", val)
-}
-
-///
-pub fn step(val: String) -> Attribute(msg) {
- attribute("step", val)
-}
-
-// INPUT TEXT AREAS ------------------------------------------------------------
-
-///
-pub fn cols(val: Int) -> Attribute(msg) {
- attribute("cols", int.to_string(val))
-}
-
-///
-pub fn rows(val: Int) -> Attribute(msg) {
- attribute("rows", int.to_string(val))
-}
-
-///
-pub fn wrap(mode: String) -> Attribute(msg) {
- attribute("wrap", mode)
-}
-
-// LINKS AND AREAS -------------------------------------------------------------
-
-///
-pub fn href(uri: String) -> Attribute(msg) {
- attribute("href", uri)
-}
-
-///
-pub fn target(target: String) -> Attribute(msg) {
- attribute("target", target)
-}
-
-///
-pub fn download(filename: String) -> Attribute(msg) {
- attribute("download", filename)
-}
-
-///
-pub fn rel(relationship: String) -> Attribute(msg) {
- attribute("rel", relationship)
-}
-
-// EMBEDDED CONTENT ------------------------------------------------------------
-
-///
-pub fn src(uri: String) -> Attribute(msg) {
- attribute("src", uri)
-}
-
-///
-pub fn height(val: Int) -> Attribute(msg) {
- property("height", int.to_string(val))
-}
-
-///
-pub fn width(val: Int) -> Attribute(msg) {
- property("width", int.to_string(val))
-}
-
-///
-pub fn alt(text: String) -> Attribute(msg) {
- attribute("alt", text)
-}
-
-// AUDIO AND VIDEO -------------------------------------------------------------
-
-///
-pub fn autoplay(should_autoplay: Bool) -> Attribute(msg) {
- property("autoplay", should_autoplay)
-}
-
-///
-pub fn controls(visible: Bool) -> Attribute(msg) {
- property("controls", visible)
-}
-
-///
-pub fn loop(should_loop: Bool) -> Attribute(msg) {
- property("loop", should_loop)
-}
diff --git a/src/lustre/effect.gleam b/src/lustre/effect.gleam
deleted file mode 100644
index 19f54b0..0000000
--- a/src/lustre/effect.gleam
+++ /dev/null
@@ -1,67 +0,0 @@
-// IMPORTS ---------------------------------------------------------------------
-
-import gleam/list
-
-// TYPES -----------------------------------------------------------------------
-
-/// A `Effect` represents some side effect we want the Lustre runtime to perform.
-/// It is parameterised by our app's `action` type because some effects need to
-/// get information back into your program.
-///
-pub opaque type Effect(action) {
- Effect(List(fn(fn(action) -> Nil) -> Nil))
-}
-
-// CONSTRUCTORS ----------------------------------------------------------------
-
-/// Create a `Effect` from some custom side effect. This is mostly useful for
-/// package authors, or for integrating other libraries into your Lustre app.
-///
-/// We pass in a function that recieves a `dispatch` callback that can be used
-/// to send messages to the Lustre runtime. We could, for example, create a `tick`
-/// command that uses the `setTimeout` JavaScript API to send a message to the
-/// runtime every second:
-///
-/// ```gleam
-/// import lustre/effect.{Effect}
-///
-/// external fn set_interval(callback: fn() -> any, interval: Int) =
-/// "" "window.setInterval"
-///
-/// pub fn every_second(msg: msg) -> Effect(msg) {
-/// use dispatch <- effect.from
-///
-/// set_interval(fn() { dispatch(msg) }, 1000)
-/// }
-/// ```
-///
-pub fn from(effect: fn(fn(action) -> Nil) -> Nil) -> Effect(action) {
- Effect([effect])
-}
-
-/// Typically our app's `update` function needs to return a tuple of
-/// `#(model, Effect(action))`. When we don't need to perform any side effects we
-/// can just return `none()`!
-///
-pub fn none() -> Effect(action) {
- Effect([])
-}
-
-// MANIPULATIONS ---------------------------------------------------------------
-
-///
-///
-pub fn batch(cmds: List(Effect(action))) -> Effect(action) {
- Effect({
- use b, Effect(a) <- list.fold(cmds, [])
- list.append(b, a)
- })
-}
-
-pub fn map(effect: Effect(a), f: fn(a) -> b) -> Effect(b) {
- let Effect(l) = effect
- Effect(list.map(
- l,
- fn(effect) { fn(dispatch) { effect(fn(a) { dispatch(f(a)) }) } },
- ))
-}
diff --git a/src/lustre/element.gleam b/src/lustre/element.gleam
deleted file mode 100644
index 4e8abee..0000000
--- a/src/lustre/element.gleam
+++ /dev/null
@@ -1,126 +0,0 @@
-// IMPORTS ---------------------------------------------------------------------
-
-import gleam/list
-import gleam/string
-import gleam/string_builder.{StringBuilder}
-import lustre/attribute.{Attribute}
-
-// TYPES -----------------------------------------------------------------------
-
-///
-///
-pub opaque type Element(msg) {
- Text(String)
- Element(String, List(Attribute(msg)), List(Element(msg)))
- ElementNs(String, List(Attribute(msg)), List(Element(msg)), String)
-}
-
-// CONSTRUCTORS ----------------------------------------------------------------
-
-///
-///
-pub fn element(
- tag: String,
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- Element(tag, attrs, children)
-}
-
-///
-///
-pub fn namespaced(
- namespace: String,
- tag: String,
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- ElementNs(tag, attrs, children, namespace)
-}
-
-///
-///
-pub fn text(content: String) -> Element(msg) {
- Text(content)
-}
-
-fn escape(escaped: String, content: String) -> String {
- case string.pop_grapheme(content) {
- Ok(#("<", xs)) -> escape(escaped <> "&lt;", xs)
- Ok(#(">", xs)) -> escape(escaped <> "&gt;", xs)
- Ok(#("&", xs)) -> escape(escaped <> "&amp;", xs)
- Ok(#("\"", xs)) -> escape(escaped <> "&quot;", xs)
- Ok(#("'", xs)) -> escape(escaped <> "&#x27;", xs)
- Ok(#(x, xs)) -> escape(escaped <> x, xs)
- Error(_) -> escaped <> content
- }
-}
-
-// MANIPULATIONS ---------------------------------------------------------------
-
-///
-///
-pub fn map(element: Element(a), f: fn(a) -> b) -> Element(b) {
- case element {
- Text(content) -> Text(content)
- Element(tag, attrs, children) ->
- Element(
- tag,
- list.map(attrs, attribute.map(_, f)),
- list.map(children, map(_, f)),
- )
- ElementNs(tag, attrs, children, namespace) ->
- ElementNs(
- tag,
- list.map(attrs, attribute.map(_, f)),
- list.map(children, map(_, f)),
- namespace,
- )
- }
-}
-
-// CONVERSIONS -----------------------------------------------------------------
-
-///
-///
-pub fn to_string(element: Element(msg)) -> String {
- to_string_builder(element)
- |> string_builder.to_string
-}
-
-///
-///
-pub fn to_string_builder(element: Element(msg)) -> StringBuilder {
- case element {
- Text(content) -> string_builder.from_string(escape("", content))
- Element(tag, attrs, children) ->
- string_builder.from_string("<" <> tag)
- |> attrs_to_string_builder(attrs)
- |> string_builder.append(">")
- |> children_to_string_builder(children)
- |> string_builder.append("</" <> tag <> ">")
- ElementNs(tag, attrs, children, namespace) ->
- string_builder.from_string("<" <> tag)
- |> attrs_to_string_builder(attrs)
- |> string_builder.append(" xmlns=\"" <> namespace <> "\"")
- |> string_builder.append(">")
- |> children_to_string_builder(children)
- |> string_builder.append("</" <> tag <> ">")
- }
-}
-
-fn attrs_to_string_builder(
- html: StringBuilder,
- attrs: List(Attribute(msg)),
-) -> StringBuilder {
- use html, attr <- list.fold(attrs, html)
- string_builder.append_builder(html, attribute.to_string_builder(attr))
-}
-
-fn children_to_string_builder(
- html: StringBuilder,
- children: List(Element(msg)),
-) -> StringBuilder {
- use html, child <- list.fold(children, html)
- string_builder.append_builder(html, to_string_builder(child))
-}
diff --git a/src/lustre/element/html.gleam b/src/lustre/element/html.gleam
deleted file mode 100644
index 9eb4f5e..0000000
--- a/src/lustre/element/html.gleam
+++ /dev/null
@@ -1,1197 +0,0 @@
-// IMPORTS ---------------------------------------------------------------------
-
-import lustre/element.{Element, element, namespaced, text}
-import lustre/attribute.{Attribute}
-
-// The doc comments (and order) for functions in this module are taken from the
-// MDN Element reference:
-//
-// https://developer.mozilla.org/en-US/docs/Web/HTML/Element
-//
-
-// HTML ELEMENTS: MAIN ROOT ----------------------------------------------------
-
-/// Represents the root (top-level element) of an HTML document, so it is also
-/// referred to as the root element. All other elements must be descendants of
-/// this element.
-///
-pub fn html(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("html", attrs, children)
-}
-
-// HTML ELEMENTS: DOCUMENT METADATA --------------------------------------------
-
-/// Specifies the base URL to use for all relative URLs in a document. There can
-/// be only one such element in a document.
-///
-pub fn base(attrs: List(Attribute(msg))) -> Element(msg) {
- element("base", attrs, [])
-}
-
-/// Contains machine-readable information (metadata) about the document, like its
-/// title, scripts, and style sheets.
-///
-pub fn head(attrs: List(Attribute(msg))) -> Element(msg) {
- element("head", attrs, [])
-}
-
-/// Specifies relationships between the current document and an external resource.
-/// This element is most commonly used to link to CSS but is also used to establish
-/// site icons (both "favicon" style icons and icons for the home screen and apps
-/// on mobile devices) among other things.
-///
-pub fn link(attrs: List(Attribute(msg))) -> Element(msg) {
- element("link", attrs, [])
-}
-
-/// Represents metadata that cannot be represented by other HTML meta-related
-/// elements, like <base>, <link>, <script>, <style> and <title>.
-///
-pub fn meta(attrs: List(Attribute(msg))) -> Element(msg) {
- element("meta", attrs, [])
-}
-
-/// Contains style information for a document or part of a document. It contains
-/// CSS, which is applied to the contents of the document containing this element.
-///
-pub fn style(attrs: List(Attribute(msg)), css: String) -> Element(msg) {
- element("style", attrs, [text(css)])
-}
-
-/// Defines the document's title that is shown in a browser's title bar or a
-/// page's tab. It only contains text; tags within the element are ignored.
-///
-pub fn title(attrs: List(Attribute(msg)), content: String) -> Element(msg) {
- element("title", attrs, [text(content)])
-}
-
-// HTML ELEMENTS: SECTIONING ROOT -----------------------------------------------
-
-/// Represents the content of an HTML document. There can be only one such element
-/// in a document.
-///
-pub fn body(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("body", attrs, children)
-}
-
-// HTML ELEMENTS: CONTENT SECTIONING -------------------------------------------
-
-/// Indicates that the enclosed HTML provides contact information for a person or
-/// people, or for an organization.
-///
-pub fn address(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("address", attrs, children)
-}
-
-/// Represents a self-contained composition in a document, page, application, or
-/// site, which is intended to be independently distributable or reusable (e.g.,
-/// in syndication). Examples include a forum post, a magazine or newspaper article,
-/// a blog entry, a product card, a user-submitted comment, an interactive widget
-/// or gadget, or any other independent item of content.
-///
-pub fn article(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("article", attrs, children)
-}
-
-/// Represents a portion of a document whose content is only indirectly related
-/// to the document's main content. Asides are frequently presented as sidebars
-/// or call-out boxes.
-///
-pub fn aside(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("aside", attrs, children)
-}
-
-/// Represents a footer for its nearest ancestor sectioning content or sectioning
-/// root element. A <footer> typically contains information about the author of
-/// the section, copyright data, or links to related documents.
-///
-pub fn footer(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("footer", attrs, children)
-}
-
-/// Represents introductory content, typically a group of introductory or navigational
-/// aids. It may contain some heading elements but also a logo, a search form, an
-/// author name, and other elements.
-///
-pub fn header(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("header", attrs, children)
-}
-
-/// Represent six levels of section headings. <h1> is the highest section level
-/// and <h6> is the lowest.
-///
-pub fn h1(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("h1", attrs, children)
-}
-
-/// Represent six levels of section headings. <h1> is the highest section level
-/// and <h6> is the lowest.
-///
-pub fn h2(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("h2", attrs, children)
-}
-
-/// Represent six levels of section headings. <h1> is the highest section level
-/// and <h6> is the lowest.
-///
-pub fn h3(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("h3", attrs, children)
-}
-
-/// Represent six levels of section headings. <h1> is the highest section level
-/// and <h6> is the lowest.
-///
-pub fn h4(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("h4", attrs, children)
-}
-
-/// Represent six levels of section headings. <h1> is the highest section level
-/// and <h6> is the lowest.
-///
-pub fn h5(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("h5", attrs, children)
-}
-
-/// Represent six levels of section headings. <h1> is the highest section level
-/// and <h6> is the lowest.
-///
-pub fn h6(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("h6", attrs, children)
-}
-
-/// Represents a heading grouped with any secondary content, such as subheadings,
-/// an alternative title, or a tagline.
-///
-pub fn hgroup(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("hgroup", attrs, children)
-}
-
-/// Represents the dominant content of the body of a document. The main content
-/// area consists of content that is directly related to or expands upon the
-/// central topic of a document, or the central functionality of an application.
-///
-pub fn main(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("main", attrs, children)
-}
-
-/// Represents a section of a page whose purpose is to provide navigation links,
-/// either within the current document or to other documents. Common examples of
-/// navigation sections are menus, tables of contents, and indexes.
-///
-pub fn nav(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("nav", attrs, children)
-}
-
-/// Represents a generic standalone section of a document, which doesn't have a
-/// more specific semantic element to represent it. Sections should always have
-/// a heading, with very few exceptions.
-///
-pub fn section(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("section", attrs, children)
-}
-
-/// Represents a part that contains a set of form controls or other content related
-/// to performing a search or filtering operation.
-///
-pub fn search(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("search", attrs, children)
-}
-
-// HTML ELEMENTS: TEXT CONTENT -------------------------------------------------
-
-/// Indicates that the enclosed text is an extended quotation. Usually, this is
-/// rendered visually by indentation. A URL for the source of the quotation may
-/// be given using the cite attribute, while a text representation of the source
-/// can be given using the <cite> element.
-///
-pub fn blockquote(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("blockquote", attrs, children)
-}
-
-/// Provides the description, definition, or value for the preceding term (<dt>)
-/// in a description list (<dl>).
-///
-pub fn dd(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("dd", attrs, children)
-}
-
-/// The generic container for flow content. It has no effect on the content or
-/// layout until styled in some way using CSS (e.g., styling is directly applied
-/// to it, or some kind of layout model like flexbox is applied to its parent
-/// element).
-///
-pub fn div(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("div", attrs, children)
-}
-
-/// Represents a description list. The element encloses a list of groups of terms
-/// (specified using the <dt> element) and descriptions (provided by <dd> elements).
-/// Common uses for this element are to implement a glossary or to display metadata
-/// (a list of key-value pairs).
-///
-pub fn dl(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("dl", attrs, children)
-}
-
-/// Specifies a term in a description or definition list, and as such must be
-/// used inside a <dl> element. It is usually followed by a <dd> element;
-/// however, multiple <dt> elements in a row indicate several terms that are
-/// all defined by the immediate next <dd> element.
-///
-pub fn dt(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("dt", attrs, children)
-}
-
-/// Represents a caption or legend describing the rest of the contents of its
-/// parent <figure> element.
-///
-pub fn figcaption(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("figcaption", attrs, children)
-}
-
-/// Represents self-contained content, potentially with an optional caption,
-/// which is specified using the <figcaption> element. The figure, its caption,
-/// and its contents are referenced as a single unit.
-///
-pub fn figure(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("figure", attrs, children)
-}
-
-/// Represents a thematic break between paragraph-level elements: for example,
-/// a change of scene in a story, or a shift of topic within a section.
-///
-pub fn hr(attrs: List(Attribute(msg))) -> Element(msg) {
- element("hr", attrs, [])
-}
-
-/// Represents an item in a list. It must be contained in a parent element: an
-/// ordered list (<ol>), an unordered list (<ul>), or a menu (<menu>). In menus
-/// and unordered lists, list items are usually displayed using bullet points.
-/// In ordered lists, they are usually displayed with an ascending counter on
-/// the left, such as a number or letter.
-///
-pub fn li(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("li", attrs, children)
-}
-
-/// A semantic alternative to <ul>, but treated by browsers (and exposed through
-/// the accessibility tree) as no different than <ul>. It represents an unordered
-/// list of items (which are represented by <li> elements).
-///
-pub fn menu(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("menu", attrs, children)
-}
-
-/// Represents an ordered list of items — typically rendered as a numbered list.
-///
-pub fn ol(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("ol", attrs, children)
-}
-
-/// Represents a paragraph. Paragraphs are usually represented in visual media
-/// as blocks of text separated from adjacent blocks by blank lines and/or
-/// first-line indentation, but HTML paragraphs can be any structural grouping
-/// of related content, such as images or form fields.
-///
-pub fn p(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("p", attrs, children)
-}
-
-/// Represents preformatted text which is to be presented exactly as written in
-/// the HTML file. The text is typically rendered using a non-proportional, or
-/// monospaced, font. Whitespace inside this element is displayed as written.
-///
-pub fn pre(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("pre", attrs, children)
-}
-
-/// Represents an unordered list of items, typically rendered as a bulleted list.
-///
-pub fn ul(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("ul", attrs, children)
-}
-
-// HTML ELEMENTS: INLINE TEXT SEMANTICS ----------------------------------------
-
-/// Together with its href attribute, creates a hyperlink to web pages, files,
-/// email addresses, locations within the current page, or anything else a URL
-/// can address.
-///
-pub fn a(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("a", attrs, children)
-}
-
-/// Represents an abbreviation or acronym.
-///
-pub fn abbr(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("abbr", attrs, children)
-}
-
-/// Used to draw the reader's attention to the element's contents, which are not
-/// otherwise granted special importance. This was formerly known as the Boldface
-/// element, and most browsers still draw the text in boldface. However, you
-/// should not use <b> for styling text or granting importance. If you wish to
-/// create boldface text, you should use the CSS font-weight property. If you
-/// wish to indicate an element is of special importance, you should use the
-/// strong element.
-///
-pub fn b(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("b", attrs, children)
-}
-
-/// Tells the browser's bidirectional algorithm to treat the text it contains in
-/// isolation from its surrounding text. It's particularly useful when a website
-/// dynamically inserts some text and doesn't know the directionality of the
-/// text being inserted.
-///
-pub fn bdi(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("bdi", attrs, children)
-}
-
-/// Overrides the current directionality of text, so that the text within is
-/// rendered in a different direction.
-///
-pub fn bdo(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("bdo", attrs, children)
-}
-
-/// Produces a line break in text (carriage-return). It is useful for writing a
-/// poem or an address, where the division of lines is significant.
-///
-pub fn br(attrs: List(Attribute(msg))) -> Element(msg) {
- element("br", attrs, [])
-}
-
-/// Used to mark up the title of a cited creative work. The reference may be in
-/// an abbreviated form according to context-appropriate conventions related to
-/// citation metadata.
-///
-pub fn cite(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("cite", attrs, children)
-}
-
-/// Displays its contents styled in a fashion intended to indicate that the text
-/// is a short fragment of computer code. By default, the content text is
-/// displayed using the user agent's default monospace font.
-///
-pub fn code(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("code", attrs, children)
-}
-
-/// Links a given piece of content with a machine-readable translation. If the
-/// content is time- or date-related, the<time> element must be used.
-///
-pub fn data(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("data", attrs, children)
-}
-
-/// Used to indicate the term being defined within the context of a definition
-/// phrase or sentence. The ancestor <p> element, the <dt>/<dd> pairing, or the
-/// nearest section ancestor of the <dfn> element, is considered to be the
-/// definition of the term.
-///
-pub fn dfn(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("dfn", attrs, children)
-}
-
-/// Marks text that has stress emphasis. The <em> element can be nested, with
-/// each nesting level indicating a greater degree of emphasis.
-///
-pub fn em(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("em", attrs, children)
-}
-
-/// Represents a range of text that is set off from the normal text for some
-/// reason, such as idiomatic text, technical terms, and taxonomical designations,
-/// among others. Historically, these have been presented using italicized type,
-/// which is the original source of the <i> naming of this element.
-///
-pub fn i(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("i", attrs, children)
-}
-
-/// Represents a span of inline text denoting textual user input from a keyboard,
-/// voice input, or any other text entry device. By convention, the user agent
-/// defaults to rendering the contents of a <kbd> element using its default
-/// monospace font, although this is not mandated by the HTML standard.
-///
-pub fn kbd(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("kbd", attrs, children)
-}
-
-/// Represents text which is marked or highlighted for reference or notation
-/// purposes due to the marked passage's relevance in the enclosing context.
-///
-pub fn mark(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("mark", attrs, children)
-}
-
-/// Indicates that the enclosed text is a short inline quotation. Most modern
-/// browsers implement this by surrounding the text in quotation marks. This
-/// element is intended for short quotations that don't require paragraph
-/// breaks; for long quotations use the <blockquote> element.
-///
-pub fn q(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("q", attrs, children)
-}
-
-/// Used to provide fall-back parentheses for browsers that do not support the
-/// display of ruby annotations using the <ruby> element. One <rp> element
-/// should enclose each of the opening and closing parentheses that wrap the
-/// <rt> element that contains the annotation's text.
-///
-pub fn rp(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("rp", attrs, children)
-}
-
-/// Specifies the ruby text component of a ruby annotation, which is used to
-/// provide pronunciation, translation, or transliteration information for East
-/// Asian typography. The <rt> element must always be contained within a <ruby>
-/// element.
-///
-pub fn rt(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("rt", attrs, children)
-}
-
-/// Represents small annotations that are rendered above, below, or next to base
-/// text, usually used for showing the pronunciation of East Asian characters.
-/// It can also be used for annotating other kinds of text, but this usage is
-/// less common.
-///
-pub fn ruby(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("ruby", attrs, children)
-}
-
-/// Renders text with a strikethrough, or a line through it. Use the <s> element
-/// to represent things that are no longer relevant or no longer accurate.
-/// However, <s> is not appropriate when indicating document edits; for that,
-/// use the del and ins elements, as appropriate.
-///
-pub fn s(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("s", attrs, children)
-}
-
-/// Used to enclose inline text which represents sample (or quoted) output from
-/// a computer program. Its contents are typically rendered using the browser's
-/// default monospaced font (such as Courier or Lucida Console).
-///
-pub fn samp(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("samp", attrs, children)
-}
-
-/// Represents side-comments and small print, like copyright and legal text,
-/// independent of its styled presentation. By default, it renders text within
-/// it one font size smaller, such as from small to x-small.
-///
-pub fn small(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("small", attrs, children)
-}
-
-/// A generic inline container for phrasing content, which does not inherently
-/// represent anything. It can be used to group elements for styling purposes
-/// (using the class or id attributes), or because they share attribute values,
-/// such as lang. It should be used only when no other semantic element is
-/// appropriate. <span> is very much like a div element, but div is a block-level
-/// element whereas a <span> is an inline-level element.
-///
-pub fn span(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("span", attrs, children)
-}
-
-/// Indicates that its contents have strong importance, seriousness, or urgency.
-/// Browsers typically render the contents in bold type.
-///
-pub fn strong(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("strong", attrs, children)
-}
-
-/// Specifies inline text which should be displayed as subscript for solely
-/// typographical reasons. Subscripts are typically rendered with a lowered
-/// baseline using smaller text.
-///
-pub fn sub(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("sub", attrs, children)
-}
-
-/// Specifies inline text which is to be displayed as superscript for solely
-/// typographical reasons. Superscripts are usually rendered with a raised
-/// baseline using smaller text.
-///
-pub fn sup(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("sup", attrs, children)
-}
-
-/// Represents a specific period in time. It may include the datetime attribute
-/// to translate dates into machine-readable format, allowing for better search
-/// engine results or custom features such as reminders.
-///
-pub fn time(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("time", attrs, children)
-}
-
-/// Represents a span of inline text which should be rendered in a way that
-/// indicates that it has a non-textual annotation. This is rendered by default
-/// as a simple solid underline but may be altered using CSS.
-///
-pub fn u(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("u", attrs, children)
-}
-
-/// Represents the name of a variable in a mathematical expression or a
-/// programming context. It's typically presented using an italicized version of
-/// the current typeface, although that behavior is browser-dependent.
-///
-pub fn var(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("var", attrs, children)
-}
-
-/// Represents the name of a variable in a mathematical expression or a
-/// programming context. It's typically presented using an italicized version of
-/// the current typeface, although that behavior is browser-dependent.
-///
-pub fn wbr(attrs: List(Attribute(msg))) -> Element(msg) {
- element("wbr", attrs, [])
-}
-
-// HTML ELEMENTS: IMAGE AND MULTIMEDIA -----------------------------------------
-
-/// Represents a word break opportunity—a position within text where the browser
-/// may optionally break a line, though its line-breaking rules would not
-/// otherwise create a break at that location.
-///
-pub fn area(attrs: List(Attribute(msg))) -> Element(msg) {
- element("area", attrs, [])
-}
-
-/// Defines an area inside an image map that has predefined clickable areas. An
-/// image map allows geometric areas on an image to be associated with hyperlink.
-///
-pub fn audio(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("audio", attrs, children)
-}
-
-/// Used to embed sound content in documents. It may contain one or more audio
-/// sources, represented using the src attribute or the source element: the
-/// browser will choose the most suitable one. It can also be the destination
-/// for streamed media, using a MediaStream.
-///
-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)),
-) -> Element(msg) {
- element("map", attrs, children)
-}
-
-/// Used as a child of the media elements, audio and video. It lets you specify
-/// timed text tracks (or time-based data), for example to automatically handle
-/// subtitles. The tracks are formatted in WebVTT format (.vtt files)—Web Video
-/// Text Tracks.
-///
-pub fn track(attrs: List(Attribute(msg))) -> Element(msg) {
- element("track", attrs, [])
-}
-
-/// Embeds a media player which supports video playback into the document. You
-/// can also use <video> for audio content, but the audio element may provide a
-/// more appropriate user experience.
-///
-pub fn video(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("video", attrs, children)
-}
-
-// HTML ELEMENTS: EMBEDDED CONTENT ---------------------------------------------
-
-/// Embeds external content at the specified point in the document. This content
-///is provided by an external application or other source of interactive content
-/// such as a browser plug-in.
-///
-pub fn embed(attrs: List(Attribute(msg))) -> Element(msg) {
- element("embed", attrs, [])
-}
-
-/// Represents a nested browsing context, embedding another HTML page into the
-/// current one.
-///
-pub fn iframe(attrs: List(Attribute(msg))) -> Element(msg) {
- element("iframe", attrs, [])
-}
-
-/// Represents an external resource, which can be treated as an image, a nested
-/// browsing context, or a resource to be handled by a plugin.
-///
-pub fn object(attrs: List(Attribute(msg))) -> Element(msg) {
- element("object", attrs, [])
-}
-
-/// Contains zero or more <source> elements and one <img> element to offer
-/// alternative versions of an image for different display/device scenarios.
-///
-pub fn picture(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("picture", attrs, children)
-}
-
-/// Enables the embedding of another HTML page into the current one to enable
-/// smoother navigation into new pages.
-///
-pub fn portal(attrs: List(Attribute(msg))) -> Element(msg) {
- element("portal", attrs, [])
-}
-
-/// Specifies multiple media resources for the picture, the audio element, or
-/// the video element. It is a void element, meaning that it has no content and
-/// does not have a closing tag. It is commonly used to offer the same media
-/// content in multiple file formats in order to provide compatibility with a
-/// broad range of browsers given their differing support for image file formats
-/// and media file formats.
-///
-pub fn source(attrs: List(Attribute(msg))) -> Element(msg) {
- element("source", attrs, [])
-}
-
-// HTML ELEMENTS: SVG AND MATHML -----------------------------------------------
-
-/// Container defining a new coordinate system and viewport. It is used as the
-/// outermost element of SVG documents, but it can also be used to embed an SVG
-/// fragment inside an SVG or HTML document.
-///
-pub fn svg(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- namespaced("http://www.w3.org/2000/svg", "svg", attrs, children)
-}
-
-/// The top-level element in MathML. Every valid MathML instance must be wrapped
-/// in it. In addition, you must not nest a second <math> element in another,
-/// but you can have an arbitrary number of other child elements in it.
-///
-pub fn math(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("math", attrs, children)
-}
-
-// HTML ELEMENTS: SCRIPTING ----------------------------------------------------
-
-/// Container element to use with either the canvas scripting API or the WebGL
-/// API to draw graphics and animations.
-///
-pub fn canvas(attrs: List(Attribute(msg))) -> Element(msg) {
- element("canvas", attrs, [])
-}
-
-/// Defines a section of HTML to be inserted if a script type on the page is
-/// unsupported or if scripting is currently turned off in the browser.
-///
-pub fn noscript(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element("noscript", attrs, children)
-}
-
-/// Used to embed executable code or data; this is typically used to embed or
-/// refer to JavaScript code. The <script> element can also be used with other
-/// languages, such as WebGL's GLSL shader programming language and JSON.
-///
-pub fn script(attrs: List(Attribute(msg)), js: String) -> Element(msg) {
- element("script", attrs, [text(js)])
-}
-
-// HTML ELEMENTS: DEMARCATING EDITS ---------------------------------------------
-
-/// Represents a range of text that has been deleted from a document. This can
-/// be used when rendering "track changes" or source code diff information, for
-/// example. The <ins> element can be used for the opposite purpose: to indicate
-/// text that has been added to the document.
-///
-pub fn del(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element.element("del", attrs, children)
-}
-
-/// Represents a range of text that has been added to a document. You can use the
-/// <del> element to similarly represent a range of text that has been deleted
-/// from the document.
-///
-pub fn ins(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element.element("ins", attrs, children)
-}
-
-// HTML ELEMENTS: TABLE CONTENT ------------------------------------------------
-
-/// Specifies the caption (or title) of a table.
-///
-pub fn caption(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element.element("caption", attrs, children)
-}
-
-/// Defines a column within a table and is used for defining common semantics on
-/// all common cells. It is generally found within a <colgroup> element.
-///
-pub fn col(attrs: List(Attribute(msg))) -> Element(msg) {
- element.element("col", attrs, [])
-}
-
-/// Defines a group of columns within a table.
-///
-pub fn colgroup(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element.element("colgroup", attrs, children)
-}
-
-/// Represents tabular data — that is, information presented in a two-dimensional
-/// table comprised of rows and columns of cells containing data.
-///
-pub fn table(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element.element("table", attrs, children)
-}
-
-/// Encapsulates a set of table rows (<tr> elements), indicating that they
-/// comprise the body of the table (<table>).
-///
-pub fn tbody(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element.element("tbody", attrs, children)
-}
-
-/// Defines a cell of a table that contains data. It participates in the table
-/// model.
-///
-pub fn td(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element.element("td", attrs, children)
-}
-
-/// Defines a set of rows summarizing the columns of the table.
-///
-pub fn tfoot(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element.element("tfoot", attrs, children)
-}
-
-/// Defines a cell as a header of a group of table cells. The exact nature of
-/// this group is defined by the scope and headers attributes.
-///
-pub fn th(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element.element("th", attrs, children)
-}
-
-/// Defines a set of rows defining the head of the columns of the table.
-///
-pub fn thead(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element.element("thead", attrs, children)
-}
-
-/// Defines a row of cells in a table. The row's cells can then be established
-/// using a mix of <td> (data cell) and <th> (header cell) elements.
-///
-pub fn tr(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element.element("tr", attrs, children)
-}
-
-// HTML ELEMENTS: FORMS --------------------------------------------------------
-
-/// An interactive element activated by a user with a mouse, keyboard, finger,
-/// voice command, or other assistive technology. Once activated, it performs an
-/// action, such as submitting a form or opening a dialog.
-///
-pub fn button(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element.element("button", attrs, children)
-}
-
-/// Contains a set of <option> elements that represent the permissible or
-/// recommended options available to choose from within other controls.
-///
-pub fn datalist(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element.element("datalist", attrs, children)
-}
-
-/// Used to group several controls as well as labels (<label>) within a web form.
-///
-pub fn fieldset(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element.element("fieldset", attrs, children)
-}
-
-/// Represents a document section containing interactive controls for submitting
-/// information.
-///
-pub fn form(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element.element("form", attrs, children)
-}
-
-/// Used to create interactive controls for web-based forms to accept data from
-/// he user; a wide variety of types of input data and control widgets are
-/// available, depending on the device and user agent. The <input> element is
-/// one of the most powerful and complex in all of HTML due to the sheer number
-/// of combinations of input types and attributes.
-///
-pub fn input(attrs: List(Attribute(msg))) -> Element(msg) {
- element.element("input", attrs, [])
-}
-
-/// Represents a caption for an item in a user interface.
-///
-pub fn label(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element.element("label", attrs, children)
-}
-
-/// Represents a caption for the content of its parent <fieldset>.
-///
-pub fn legend(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element.element("legend", attrs, children)
-}
-
-/// Represents either a scalar value within a known range or a fractional value.
-///
-pub fn meter(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element.element("meter", attrs, children)
-}
-
-/// Creates a grouping of options within a <select> element.
-///
-pub fn optgroup(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element.element("optgroup", attrs, children)
-}
-
-/// Used to define an item contained in a select, an <optgroup>, or a <datalist>
-/// element. As such, <option> can represent menu items in popups and other lists
-/// of items in an HTML document.
-///
-pub fn option(attrs: List(Attribute(msg))) -> Element(msg) {
- element.element("option", attrs, [])
-}
-
-/// Container element into which a site or app can inject the results of a
-/// calculation or the outcome of a user action.
-///
-pub fn output(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element.element("output", attrs, children)
-}
-
-/// Displays an indicator showing the completion progress of a task, typically
-/// displayed as a progress bar.
-///
-pub fn progress(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element.element("progress", attrs, children)
-}
-
-/// Represents a control that provides a menu of options.
-///
-pub fn select(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element.element("select", attrs, children)
-}
-
-/// Represents a multi-line plain-text editing control, useful when you want to
-/// allow users to enter a sizeable amount of free-form text, for example, a
-/// comment on a review or feedback form.
-///
-pub fn textarea(attrs: List(Attribute(msg))) -> Element(msg) {
- element.element("textarea", attrs, [])
-}
-
-// HTML ELEMENTS: INTERACTIVE ELEMENTS -----------------------------------------
-
-/// Creates a disclosure widget in which information is visible only when the
-/// widget is toggled into an "open" state. A summary or label must be provided
-/// using the <summary> element.
-///
-pub fn details(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element.element("details", attrs, children)
-}
-
-/// Represents a dialog box or other interactive component, such as a dismissible
-/// alert, inspector, or subwindow.
-///
-pub fn dialog(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element.element("dialog", attrs, children)
-}
-
-/// Specifies a summary, caption, or legend for a details element's disclosure box.
-/// Clicking the <summary> element toggles the state of the parent <details> element
-/// open and closed.
-///
-pub fn summary(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element.element("summary", attrs, children)
-}
-
-// HTML ELEMENTS: WEB COMPONENTS -----------------------------------------------
-
-/// Part of the Web Components technology suite, this element is a placeholder
-/// inside a web component that you can fill with your own markup, which lets you
-/// create separate DOM trees and present them together.
-///
-pub fn slot(attrs: List(Attribute(msg))) -> Element(msg) {
- element.element("slot", attrs, [])
-}
-
-/// A mechanism for holding HTML that is not to be rendered immediately when a
-/// page is loaded but may be instantiated subsequently during runtime using
-/// JavaScript.
-///
-pub fn template(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- element.element("template", attrs, children)
-}
diff --git a/src/lustre/element/svg.gleam b/src/lustre/element/svg.gleam
deleted file mode 100644
index f9776ca..0000000
--- a/src/lustre/element/svg.gleam
+++ /dev/null
@@ -1,351 +0,0 @@
-// IMPORTS ---------------------------------------------------------------------
-
-import lustre/element.{Element, namespaced, text as inline_text}
-import lustre/attribute.{Attribute}
-
-// CONSTANTS -------------------------------------------------------------------
-
-const namespace = "http://www.w3.org/2000/svg"
-
-// The doc comments (and order) for functions in this module are taken from the
-// MDN Element reference:
-//
-// https://developer.mozilla.org/en-US/docs/Web/SVG/Element
-//
-
-// SVG ELEMENTS: ANIMATION ELEMENTS --------------------------------------------
-
-pub fn animate(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "animate", attrs, [])
-}
-
-pub fn animate_motion(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "animateMotion", attrs, [])
-}
-
-pub fn animate_transform(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "animateTransform", attrs, [])
-}
-
-pub fn mpath(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "mpath", attrs, [])
-}
-
-pub fn set(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "set", attrs, [])
-}
-
-// SVG ELEMENTS: BASIC SHAPES --------------------------------------------------
-
-pub fn circle(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "circle", attrs, [])
-}
-
-pub fn ellipse(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "ellipse", attrs, [])
-}
-
-pub fn line(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "line", attrs, [])
-}
-
-pub fn polygon(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "polygon", attrs, [])
-}
-
-pub fn polyline(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "polyline", attrs, [])
-}
-
-pub fn rect(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "rect", attrs, [])
-}
-
-// SVG ELEMENTS: CONTAINER ELEMENTS --------------------------------------------
-
-pub fn a(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- namespaced(namespace, "a", attrs, children)
-}
-
-pub fn defs(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- namespaced(namespace, "defs", attrs, children)
-}
-
-pub fn g(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- namespaced(namespace, "g", attrs, children)
-}
-
-pub fn marker(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- namespaced(namespace, "marker", attrs, children)
-}
-
-pub fn mask(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- namespaced(namespace, "mask", attrs, children)
-}
-
-pub fn missing_glyph(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- namespaced(namespace, "missing-glyph", attrs, children)
-}
-
-pub fn pattern(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- namespaced(namespace, "pattern", attrs, children)
-}
-
-pub fn svg(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- namespaced(namespace, "svg", attrs, children)
-}
-
-pub fn switch(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- namespaced(namespace, "switch", attrs, children)
-}
-
-pub fn symbol(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- namespaced(namespace, "symbol", attrs, children)
-}
-
-// SVG ELEMENTS: DESCRIPTIVE ELEMENTS ------------------------------------------
-
-pub fn desc(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- namespaced(namespace, "desc", attrs, children)
-}
-
-pub fn metadata(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- namespaced(namespace, "metadata", attrs, children)
-}
-
-pub fn title(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- namespaced(namespace, "title", attrs, children)
-}
-
-// SVG ELEMENTS: FILTER EFFECTS ------------------------------------------------
-
-pub fn fe_blend(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "feBlend", attrs, [])
-}
-
-pub fn fe_color_matrix(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "feColorMatrix", attrs, [])
-}
-
-pub fn fe_component_transfer(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "feComponentTransfer", attrs, [])
-}
-
-pub fn fe_composite(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "feComposite", attrs, [])
-}
-
-pub fn fe_convolve_matrix(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "feConvolveMatrix", attrs, [])
-}
-
-pub fn fe_diffuse_lighting(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- namespaced(namespace, "feDiffuseLighting", attrs, children)
-}
-
-pub fn fe_displacement_map(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "feDisplacementMap", attrs, [])
-}
-
-pub fn fe_drop_shadow(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "feDropShadow", attrs, [])
-}
-
-pub fn fe_flood(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "feFlood", attrs, [])
-}
-
-pub fn fe_func_a(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "feFuncA", attrs, [])
-}
-
-pub fn fe_func_b(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "feFuncB", attrs, [])
-}
-
-pub fn fe_func_g(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "feFuncG", attrs, [])
-}
-
-pub fn fe_func_r(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "feFuncR", attrs, [])
-}
-
-pub fn fe_gaussian_blur(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "feGaussianBlur", attrs, [])
-}
-
-pub fn fe_image(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "feImage", attrs, [])
-}
-
-pub fn fe_merge(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- namespaced(namespace, "feMerge", attrs, children)
-}
-
-pub fn fe_merge_node(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "feMergeNode", attrs, [])
-}
-
-pub fn fe_morphology(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "feMorphology", attrs, [])
-}
-
-pub fn fe_offset(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "feOffset", attrs, [])
-}
-
-pub fn fe_specular_lighting(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- namespaced(namespace, "feSpecularLighting", attrs, children)
-}
-
-pub fn fe_tile(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- namespaced(namespace, "feTile", attrs, children)
-}
-
-pub fn fe_turbulence(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "feTurbulence", attrs, [])
-}
-
-// SVG ELEMENTS: GRADIENT ELEMENTS ---------------------------------------------
-
-pub fn linear_gradient(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- namespaced(namespace, "linearGradient", attrs, children)
-}
-
-pub fn radial_gradient(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- namespaced(namespace, "radialGradient", attrs, children)
-}
-
-pub fn stop(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "stop", attrs, [])
-}
-
-// SVG ELEMENTS: GRAPHICAL ELEMENTS --------------------------------------------
-
-pub fn image(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "image", attrs, [])
-}
-
-pub fn path(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "path", attrs, [])
-}
-
-pub fn text(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "text", attrs, [])
-}
-
-pub fn use_(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "use", attrs, [])
-}
-
-// SVG ELEMENTS: LIGHTING ELEMENTS ---------------------------------------------
-
-pub fn fe_distant_light(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "feDistantLight", attrs, [])
-}
-
-pub fn fe_point_light(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "fePointLight", attrs, [])
-}
-
-pub fn fe_spot_light(attrs: List(Attribute(msg))) -> Element(msg) {
- namespaced(namespace, "feSpotLight", attrs, [])
-}
-
-// SVG ELEMENTS: NEVER-RENDERED ELEMENTS ---------------------------------------
-
-pub fn clip_path(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- namespaced(namespace, "clipPath", attrs, children)
-}
-
-pub fn script(attrs: List(Attribute(msg)), js: String) -> Element(msg) {
- namespaced(namespace, "script", attrs, [inline_text(js)])
-}
-
-pub fn style(attrs: List(Attribute(msg)), css: String) -> Element(msg) {
- namespaced(namespace, "style", attrs, [inline_text(css)])
-}
-
-// SVG ELEMENTS: RENDERABLE ELEMENTS -------------------------------------------
-
-pub fn foreign_object(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- namespaced(namespace, "foreignObject", attrs, children)
-}
-
-pub fn text_path(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- namespaced(namespace, "textPath", attrs, children)
-}
-
-pub fn tspan(
- attrs: List(Attribute(msg)),
- children: List(Element(msg)),
-) -> Element(msg) {
- namespaced(namespace, "tspan", attrs, children)
-}
diff --git a/src/lustre/event.gleam b/src/lustre/event.gleam
deleted file mode 100644
index 8ee2f4f..0000000
--- a/src/lustre/event.gleam
+++ /dev/null
@@ -1,184 +0,0 @@
-////
-
-// IMPORTS ---------------------------------------------------------------------
-
-import gleam/dynamic.{DecodeError, Dynamic}
-import gleam/option.{None, Option, Some}
-import gleam/result
-import lustre/attribute.{Attribute}
-import lustre/effect.{Effect}
-
-// TYPES -----------------------------------------------------------------------
-
-type Decoded(a) =
- Result(a, List(DecodeError))
-
-// EFFECTS ---------------------------------------------------------------------
-
-@target(javascript)
-@external(javascript, "../lustre.ffi.mjs", "emit")
-pub fn emit(event: String, data: any) -> Effect(msg)
-
-// CUSTOM EVENTS ---------------------------------------------------------------
-
-pub fn on(name: String, handler: fn(Dynamic) -> Option(msg)) -> Attribute(msg) {
- attribute.on(name, handler)
-}
-
-// MOUSE EVENTS ----------------------------------------------------------------
-
-///
-pub fn on_click(msg: msg) -> Attribute(msg) {
- use _ <- on("click")
- Some(msg)
-}
-
-///
-pub fn on_mouse_down(msg: msg) -> Attribute(msg) {
- use _ <- on("mousedown")
- Some(msg)
-}
-
-///
-pub fn on_mouse_up(msg: msg) -> Attribute(msg) {
- use _ <- on("mouseup")
- Some(msg)
-}
-
-///
-pub fn on_mouse_enter(msg: msg) -> Attribute(msg) {
- use _ <- on("mouseenter")
- Some(msg)
-}
-
-///
-pub fn on_mouse_leave(msg: msg) -> Attribute(msg) {
- use _ <- on("mouseleave")
- Some(msg)
-}
-
-///
-pub fn on_mouse_over(msg: msg) -> Attribute(msg) {
- use _ <- on("mouseover")
- Some(msg)
-}
-
-///
-pub fn on_mouse_out(msg: msg) -> Attribute(msg) {
- use _ <- on("mouseout")
- Some(msg)
-}
-
-// KEYBOARD EVENTS -------------------------------------------------------------
-
-/// Listens for key presses on an element, and dispatches a message with the
-/// current key being pressed.
-///
-pub fn on_keypress(msg: fn(String) -> msg) -> Attribute(msg) {
- use event <- on("keypress")
-
- case dynamic.field("key", dynamic.string)(event) {
- Ok(key) -> Some(msg(key))
- Error(_) -> None
- }
-}
-
-/// Listens for key dow events on an element, and dispatches a message with the
-/// current key being pressed.
-///
-pub fn on_keydown(msg: fn(String) -> msg) -> Attribute(msg) {
- use event <- on("keydown")
-
- case dynamic.field("key", dynamic.string)(event) {
- Ok(key) -> Some(msg(key))
- Error(_) -> None
- }
-}
-
-/// Listens for key up events on an element, and dispatches a message with the
-/// current key being released.
-///
-pub fn on_keyup(msg: fn(String) -> msg) -> Attribute(msg) {
- use event <- on("keyup")
-
- case dynamic.field("key", dynamic.string)(event) {
- Ok(key) -> Some(msg(key))
- Error(_) -> None
- }
-}
-
-// FORM EVENTS -----------------------------------------------------------------
-
-///
-pub fn on_input(msg: fn(String) -> msg) -> Attribute(msg) {
- use event <- on("input")
-
- case value(event) {
- Ok(val) -> Some(msg(val))
- Error(_) -> None
- }
-}
-
-pub fn on_check(msg: fn(Bool) -> msg) -> Attribute(msg) {
- use event <- on("change")
-
- case checked(event) {
- Ok(val) -> Some(msg(val))
- Error(_) -> None
- }
-}
-
-pub fn on_submit(msg: msg) -> Attribute(msg) {
- use _ <- on("submit")
- Some(msg)
-}
-
-// FOCUS EVENTS ----------------------------------------------------------------
-
-pub fn on_focus(msg: msg) -> Attribute(msg) {
- use _ <- on("focus")
- Some(msg)
-}
-
-pub fn on_blur(msg: msg) -> Attribute(msg) {
- use _ <- on("blur")
- Some(msg)
-}
-
-// DECODERS --------------------------------------------------------------------
-
-/// A helpful decoder to extract the `value` from an event object. This is handy
-/// for getting the value as a string from an input event, for example.
-///
-pub fn value(event: Dynamic) -> Decoded(String) {
- event
- |> dynamic.field("target", dynamic.field("value", dynamic.string))
-}
-
-/// A helpful decoder to extract the `checked` property from an event triggered
-/// by a checkbox.
-///
-pub fn checked(event: Dynamic) -> Decoded(Bool) {
- event
- |> dynamic.field("target", dynamic.field("checked", dynamic.bool))
-}
-
-/// A helpful decoder to grab the mouse's current x and y position in the
-/// viewport from an event object.
-///
-pub fn mouse_position(event: Dynamic) -> Decoded(#(Float, Float)) {
- use x <- result.then(dynamic.field("clientX", dynamic.float)(event))
- use y <- result.then(dynamic.field("clientY", dynamic.float)(event))
-
- Ok(#(x, y))
-}
-
-// UTILS -----------------------------------------------------------------------
-
-@target(javascript)
-@external(javascript, "../lustre.ffi.mjs", "prevent_default")
-pub fn prevent_default(event: Dynamic) -> Nil
-
-@target(javascript)
-@external(javascript, "../lustre.ffi.mjs", "stop_propagation")
-pub fn stop_propagation(event: Dynamic) -> Nil
diff --git a/src/runtime.ffi.mjs b/src/runtime.ffi.mjs
deleted file mode 100644
index 7325583..0000000
--- a/src/runtime.ffi.mjs
+++ /dev/null
@@ -1,230 +0,0 @@
-import { element, namespaced, text } from "./lustre/element.mjs";
-import { List, Empty } from "./gleam.mjs";
-import { Some, None } from "../gleam_stdlib/gleam/option.mjs";
-
-const Element = element("").constructor;
-const ElementNs = namespaced("", "").constructor;
-const Text = text("").constructor;
-
-export function morph(prev, curr, parent) {
- if (curr instanceof ElementNs)
- return prev?.nodeType === 1 &&
- prev.nodeName === curr[0].toUpperCase() &&
- prev.namespaceURI === curr[3]
- ? morphElement(prev, curr, curr[3], parent)
- : createElement(prev, curr, curr[3], parent);
-
- if (curr instanceof Element) {
- return prev?.nodeType === 1 && prev.nodeName === curr[0].toUpperCase()
- ? morphElement(prev, curr, null, parent)
- : createElement(prev, curr, null, parent);
- }
-
- if (curr instanceof Text) {
- return prev?.nodeType === 3
- ? morphText(prev, curr)
- : createText(prev, curr);
- }
-
- return document.createComment(
- [
- "[internal lustre error] I couldn't work out how to render this element. This",
- "function should only be called internally by lustre's runtime: if you think",
- "this is an error, please open an issue at",
- "https://github.come/hayleigh-dot-dev/lustre/issues/new.",
- ].join(" ")
- );
-}
-
-// ELEMENTS --------------------------------------------------------------------
-
-function createElement(prev, curr, ns, parent = null) {
- const el = ns
- ? document.createElementNS(ns, curr[0])
- : document.createElement(curr[0]);
-
- let attr = curr[1];
- while (attr.head) {
- morphAttr(el, attr.head[0], attr.head[1]);
- attr = attr.tail;
- }
-
- if (customElements.get(curr[0])) {
- el._slot = curr[2];
- } else if (curr[0] === "slot") {
- let child = new Empty();
- let parentWithSlot = parent;
-
- while (parentWithSlot) {
- if (parentWithSlot._slot) {
- child = parentWithSlot._slot;
- break;
- } else {
- parentWithSlot = parentWithSlot.parentNode;
- }
- }
-
- while (child.head) {
- el.appendChild(morph(null, child.head, el));
- child = child.tail;
- }
- } else {
- let child = curr[2];
- while (child.head) {
- el.appendChild(morph(null, child.head, el));
- child = child.tail;
- }
-
- if (prev) prev.replaceWith(el);
- }
- return el;
-}
-
-function morphElement(prev, curr, ns, parent) {
- const prevAttrs = prev.attributes;
- const currAttrs = new Map();
-
- let currAttr = curr[1];
- while (currAttr.head) {
- currAttrs.set(currAttr.head[0], currAttr.head[1]);
- currAttr = currAttr.tail;
- }
-
- for (const { name, value: prevValue } of prevAttrs) {
- if (!currAttrs.has(name)) {
- prev.removeAttribute(name);
- } else {
- const value = currAttrs.get(name);
-
- if (value !== prevValue) {
- morphAttr(prev, name, value);
- currAttrs.delete(name);
- }
- }
- }
-
- for (const [name, value] of currAttrs) {
- morphAttr(prev, name, value);
- }
-
- if (customElements.get(curr[0])) {
- prev._slot = curr[2];
- } else if (curr[0] === "slot") {
- let prevChild = prev.firstChild;
- let currChild = new Empty();
- let parentWithSlot = parent;
-
- while (parentWithSlot) {
- if (parentWithSlot._slot) {
- currChild = parentWithSlot._slot;
- break;
- } else {
- parentWithSlot = parentWithSlot.parentNode;
- }
- }
-
- while (prevChild) {
- if (currChild.head) {
- morph(prevChild, currChild.head, prev);
- currChild = currChild.tail;
- }
-
- prevChild = prevChild.nextSibling;
- }
-
- while (currChild.head) {
- prev.appendChild(morph(null, currChild.head, prev));
- currChild = currChild.tail;
- }
- } else {
- let prevChild = prev.firstChild;
- let currChild = curr[2];
-
- while (prevChild) {
- if (currChild.head) {
- const next = prevChild.nextSibling;
- morph(prevChild, currChild.head, prev);
- currChild = currChild.tail;
- prevChild = next;
- } else {
- const next = prevChild.nextSibling;
- prevChild.remove();
- prevChild = next;
- }
- }
-
- while (currChild.head) {
- prev.appendChild(morph(null, currChild.head, prev));
- currChild = currChild.tail;
- }
- }
-
- return prev;
-}
-
-// ATTRIBUTES ------------------------------------------------------------------
-
-function morphAttr(el, name, value) {
- switch (typeof value) {
- case "string":
- el.setAttribute(name, value);
- break;
-
- // Boolean attributes work a bit differently in HTML. Their presence always
- // implies true: to set an attribute to false you need to remove it entirely.
- case "boolean":
- value ? el.setAttribute(name, name) : el.removeAttribute(name);
- break;
-
- // Event listeners need to be handled slightly differently because we need
- // to be able to support custom events. We
- case name.startsWith("on") && "function": {
- const event = name.slice(2).toLowerCase();
-
- if (el[`_${name}`] === value) break;
-
- el.removeEventListener(event, el[`_${name}`]);
- el.addEventListener(event, value);
- el[`_${name}`] = value;
- break;
- }
-
- default: {
- el[name] = toJsValue(value);
- }
- }
-}
-
-function toJsValue(value) {
- if (value instanceof List) {
- return value.toArray().map(toJsValue);
- }
-
- if (value instanceof Some) {
- return toJsValue(value[0]);
- }
-
- if (value instanceof None) {
- return null;
- }
-
- return value;
-}
-
-// TEXT ------------------------------------------------------------------------
-
-function createText(prev, curr) {
- const el = document.createTextNode(curr[0]);
-
- if (prev) prev.replaceWith(el);
- return el;
-}
-
-function morphText(prev, curr) {
- const prevValue = prev.nodeValue;
- const currValue = curr[0];
-
- if (prevValue !== currValue) prev.nodeValue = currValue;
-
- return prev;
-}