README

#Html

This package provides the helper functions to build Html.

Here are some design choices:

  • Guide Users with Type Definitions

    This package defines specific types for arguments instead of relying on a potentially confusing String type. These types serve as documentation, helping users understand how to correctly provide arguments.

  • Seamless HTML EDSL

    Instead of relying on a precompiler to process JSX-like syntax, this EDSL allows users to leverage the full power of Moonbit's syntax and toolchain.

    You can embed expressions in views without needing escape characters like property={expression} or :property=expression. For cases where the property name matches the variable name, you can use the convenient name-punning syntax, such as property? or property~.

    In this early stage, we need to focus on improving the functionality of Rabbit-TEA. Language extensions like JSX may be considered after the 1.0 release.

  • Keep it simple

    No compile-time code translation or runtime reflection magic.

#Using the Html EDSL

We are trying to define wrapper functions for each HTML element. They all follow a form like this:

pub fn div[M](
style~ : Array[String] = [],
id? : String,
klass? : String,
click? : M,
children : Array[Html[M]]
) -> Html[M]

#The text element

To represent text in HTML, use the text function.

let html = p([text("hello world")])

#The special nothing element

There is a special nothing element that does not represent an actual HTML element, it simply represents "nothing". This is particularly useful for handling multiple Option types in your model:

fn bar(path : Path, tag : Option[Tag]){
let path = foo(path)
let tag = match tag {
None => []
Some(x) => [view(x)] // Yuck!
}
div([path] + tag) // Don't do this
}

fn bar(path : Path, tag : Option[Tag]){
let path = foo(path)
let tag = match tag {
None => nothing()
Some(x) => view(x)
}
div([path, tag]) // Use @html.nothing
}

#Advanced Usage

The wrapper functions and properties provided here may not cover all possible use cases. If you encounter missing functionality, feel free to file an issue or use the node() function as a workaround. The node() function allows you to manually specify the tag name, attributes, and children for your HTML element, offering flexibility for advanced or uncommon scenarios.

let html = node(
"div",
[style("key","value"), property("id","key")],
[child1, child2],
)

Contributions to help us finish the missing wrappers or arguments are also welcome.

#
Attribute

type Attribute[Msg]

#
AutoComplete

pub(all) enum AutoComplete {
On
Off
}

#
Html

#alias(T)
pub(all) type Html[Msg]
Node
[Msg]

#
Html::inner

#deprecated("Use `struct T(A)` to declare a newtype and use `.0` access the underlying type instead.")
fn[Msg] Html::inner(self : Html[Msg]) ->
Node
[Msg]
Convert newtype to its underlying type, automatically derived.

#
Html::map

fn[A, B] Html::map(self : Html[A], f : (A) -> B) -> Html[B]

Convert msg type of Html.

This is a expensive operation and should be used rarely.

#
Html::to_virtual_dom

fn[Msg] Html::to_virtual_dom(self : Html[Msg]) ->
Node
[Msg]

#
InputType

pub(all) enum InputType {
Button
Checkbox
Color
Date
DateTimeLocal
Email
File
Hidden
Image
Month
Number
Password
Radio
Range
Reset
Search
Submit
Tel
Text
Time
Url
Week
}

#
Mouse

type Mouse

#
Mouse::client_pos

fn Mouse::client_pos(self : Mouse) -> Pos

#
Mouse::offset_pos

fn Mouse::offset_pos(self : Mouse) -> Pos

#
Mouse::screen_pos

fn Mouse::screen_pos(self : Mouse) -> Pos

#
Pos

pub(all) struct Pos {
x : Int
y : Int
}

#
Scope

pub(all) enum Scope {
Row
Col
RowGroup
ColGroup
}

#
Scroll

type Scroll

#
Scroll::height

fn Scroll::height(self : Scroll) -> Int

#
Scroll::offset_pos

fn Scroll::offset_pos(self : Scroll) -> Pos

#
Scroll::width

fn Scroll::width(self : Scroll) -> Int

#
Target

pub(all) enum Target {
Self
Blank
}

fn[M] a(style? : Array[String], id? : String, class? : String, href~ : String, target? : Target, children : Array[Html[M]], escape? : Bool) -> Html[M]

Create a element.

Parameters

  • escape: an optional boolean that determines whether the link should be escaped. By default, it is set to false.

    When escape is set to true, clicking the escaped link will cause the browser to immediately navigate to the href target, bypassing any interception logic. As a result, the UrlRequest message will not be triggered.

  • target: an optional Target that specifies where to open the link.

If the user holds the ctrl key (or the command key on macOS) while clicking the link, the click event will be handled by the browser, and the UrlRequest will not be triggered either.

#
attribute

fn[Msg] attribute(key : String, value : String) -> Attribute[Msg]

fn[M] b(style? : Array[String], id? : String, class? : String, children : Array[Html[M]]) -> Html[M]

#
blockquote

fn[M] blockquote(style? : Array[String], id? : String, class? : String, children : Array[Html[M]]) -> Html[M]

fn[M] br(style? : Array[String], id? : String, class? : String) -> Html[M]

#
button

fn[M] button(style? : Array[String], id? : String, class? : String, click? : M, children : Array[Html[M]]) -> Html[M]

#
caption

fn[M] caption(style? : Array[String], id? : String, class? : String, children : Array[Html[M]]) -> Html[M]

#
code

fn[M] code(style? : Array[String], id? : String, class? : String, children : Array[Html[M]]) -> Html[M]

#
col

fn[M] col(style? : Array[String], id? : String, span? : Int, class? : String, children : Array[Html[M]]) -> Html[M]

#
colgroup

fn[M] colgroup(style? : Array[String], id? : String, span? : Int, class? : String, children : Array[Html[M]]) -> Html[M]

fn[M] dd(style? : Array[String], id? : String, class? : String, children : Array[Html[M]]) -> Html[M]

#
dialog

fn[M] dialog(style? : Array[String], id? : String, class? : String, open? : Bool, close? : (String) -> M, cancel? : M, children : Array[Html[M]]) -> Html[M]

Dialog element.

Hint: You can use the show and close commands in the @dialog package to manipulate the dialog.

Attributes

  • open: indicates whether the dialog is open or closed by default.

Messages

  • close: triggered when the dialog is closed.

    The string payload of the close message is the return value of the dialog.

  • cancel: triggered when the user instructs the browser that they wish to dismiss the current open dialog.

    It can be triggered when the user presses esc or uses the request_close command.

    If the cancel argument is provided, the dialog will not close automatically after this message is triggered. You can use the @dialog.close command to close it or @cmd.none to keep it open.

Example

typealias Model = String

enum Msg {
Open
Closed(String)
YesNo(Bool)
}

fn update(msg : Msg, model : Model) -> (Cmd[Msg], Model) {
match msg {
Open => (@dialog.show("confirm"), model)
YesNo(answer) => (@dialog.close("confirm", return_value=answer.to_string()), model)
Closed(value) => (none(), value)
}
}

fn view(model : Model) -> Html[Msg] {
div([
h1([text(model)]),
button(click=Msg::Open, [text("open")]),
dialog(id="confirm", close=Msg::Closed, [
p([text("Are you sure?")]),
button(click=YesNo(true), [text("Yes")]),
button(click=YesNo(false), [text("No")]),
]),
])
}

#
div

fn[M] div(style? : Array[String], id? : String, class? : String, click? : M, children : Array[Html[M]]) -> Html[M]

fn[M] dl(style? : Array[String], id? : String, class? : String, children : Array[Html[M]]) -> Html[M]

fn[M] dt(style? : Array[String], id? : String, class? : String, children : Array[Html[M]]) -> Html[M]

fn[M] em(style? : Array[String], id? : String, class? : String, children : Array[Html[M]]) -> Html[M]

#
external

fn[Msg] external(node :
Node
, attrs : Ref[Array[Attribute[Msg]]?], width~ : Int, height~ : Int) -> Html[Msg]

#
form

fn[M] form(style? : Array[String], id? : String, class? : String, action? : String, name? : String, children : Array[Html[M]]) -> Html[M]

fn[M] h1(style? : Array[String], id? : String, class? : String, children : Array[Html[M]]) -> Html[M]

fn[M] h2(style? : Array[String], id? : String, class? : String, children : Array[Html[M]]) -> Html[M]

fn[M] h3(style? : Array[String], id? : String, class? : String, children : Array[Html[M]]) -> Html[M]

fn[M] h4(style? : Array[String], id? : String, class? : String, children : Array[Html[M]]) -> Html[M]

fn[M] h5(style? : Array[String], id? : String, class? : String, children : Array[Html[M]]) -> Html[M]

fn[M] h6(style? : Array[String], id? : String, class? : String, children : Array[Html[M]]) -> Html[M]

fn[M] hr(style? : Array[String], id? : String, class? : String, children? : Array[Html[M]]) -> Html[M]

#
href

fn[Msg] href(value : String) -> Attribute[Msg]

fn[M] i(style? : Array[String], id? : String, class? : String, children : Array[Html[M]]) -> Html[M]

#
iframe

fn[M] iframe(style? : Array[String], id? : String, class? : String, src? : String, title? : String, width? : Int, height? : Int) -> Html[M]

#
img

fn[M] img(style? : Array[String], id? : String, class? : String, src? : String, alt? : String, title? : String, width? : Int, height? : Int, border? : Int, children : Array[Html[M]]) -> Html[M]

#
input

fn[M] input(input_type? : InputType, name? : String, value? : String, checked? : Bool, read_only? : Bool, multiple? : Bool, accept? : String, placeholder? : String, auto_complete? : AutoComplete, style? : Array[String], max? : Int, min? : Int, step? : Int, maxlength? : Int, minlength? : Int, pattern? : String, size? : Int, width? : Int, height? : Int, id? : String, class? : String, children? : Array[Html[M]], change? : (String) -> M, input? : (String) -> M) -> Html[M]

#
label

fn[M] label(style? : Array[String], id? : String, class? : String, for_? : String, children : Array[Html[M]]) -> Html[M]

fn[M] li(style? : Array[String], value? : Int, id? : String, class? : String, click? : M, children : Array[Html[M]]) -> Html[M]

#
node

fn[Msg] node(tag : String, attributes : Array[Attribute[Msg]], children : Array[Html[Msg]]) -> Html[Msg]

#
nothing

fn[M] nothing() -> Html[M]

Represents an empty element
fn[M] ol(style? : Array[String], reversed? : Bool, start? : Int, id? : String, class? : String, children : Array[Html[M]]) -> Html[M]

Notice that the type attribute for ol is not important for the browser now. If you want to change the type of the list, you should use the list-style-type property in CSS.

#
on_change

fn[Msg] on_change(msg : (String) -> Msg) -> Attribute[Msg]

#
on_click

fn[Msg] on_click(msg : (Mouse) -> Msg) -> Attribute[Msg]

#
on_double_click

fn[Msg] on_double_click(msg : (Mouse) -> Msg) -> Attribute[Msg]

#
on_input

fn[Msg] on_input(msg : (String) -> Msg) -> Attribute[Msg]

#
on_mouse_down

fn[Msg] on_mouse_down(msg : (Mouse) -> Msg) -> Attribute[Msg]

#
on_mouse_enter

fn[Msg] on_mouse_enter(msg : (Mouse) -> Msg) -> Attribute[Msg]

#
on_mouse_leave

fn[Msg] on_mouse_leave(msg : (Mouse) -> Msg) -> Attribute[Msg]

#
on_mouse_move

fn[Msg] on_mouse_move(msg : (Mouse) -> Msg) -> Attribute[Msg]

#
on_mouse_out

fn[Msg] on_mouse_out(msg : (Mouse) -> Msg) -> Attribute[Msg]

#
on_mouse_over

fn[Msg] on_mouse_over(msg : (Mouse) -> Msg) -> Attribute[Msg]

#
on_mouse_up

fn[Msg] on_mouse_up(msg : (Mouse) -> Msg) -> Attribute[Msg]

#
on_scroll

fn[M] on_scroll(msg : (Scroll) -> M) -> Attribute[M]

#
option

fn[M] option(style? : Array[String], id? : String, class? : String, disabled? : Bool, value? : String, selected? : Bool, children : Array[Html[M]]) -> Html[M]

fn[M] p(style? : Array[String], id? : String, class? : String, children : Array[Html[M]]) -> Html[M]

#
pre

fn[M] pre(style? : Array[String], id? : String, class? : String, children : Array[Html[M]]) -> Html[M]

#
property

fn[M] property(key : String, value :
Variant
) -> Attribute[M]

#
section

fn[M] section(style? : Array[String], id? : String, class? : String, children : Array[Html[M]]) -> Html[M]

#
select

fn[M] select(style? : Array[String], id? : String, class? : String, disabled? : Bool, name? : String, change? : (String) -> M, children : Array[Html[M]]) -> Html[M]

#
span

fn[M] span(style? : Array[String], id? : String, class? : String, children : Array[Html[M]]) -> Html[M]

#
strong

fn[M] strong(style? : Array[String], id? : String, class? : String, children : Array[Html[M]]) -> Html[M]

#
style

fn[Msg] style(key : String, value : String) -> Attribute[Msg]

Specify an style

#
sub

fn[M] sub(style? : Array[String], id? : String, class? : String, children : Array[Html[M]]) -> Html[M]

#
sup

fn[M] sup(style? : Array[String], id? : String, class? : String, children : Array[Html[M]]) -> Html[M]

#
table

fn[M] table(style? : Array[String], id? : String, class? : String, children : Array[Html[M]]) -> Html[M]

#
target

fn[Msg] target(value : Target) -> Attribute[Msg]

#
tbody

fn[M] tbody(style? : Array[String], id? : String, class? : String, children : Array[Html[M]]) -> Html[M]

fn[M] td(style? : Array[String], id? : String, colspan? : Int, rowspan? : Int, headers? : String, class? : String, children : Array[Html[M]]) -> Html[M]

#
text

fn[Msg] text(str : String) -> Html[Msg]

#
tfoot

fn[M] tfoot(style? : Array[String], id? : String, class? : String, children : Array[Html[M]]) -> Html[M]

fn[M] th(style? : Array[String], id? : String, abbr? : String, colspan? : Int, rowspan? : Int, headers? : String, scope? : Scope, class? : String, children : Array[Html[M]]) -> Html[M]

#
thead

fn[M] thead(style? : Array[String], id? : String, class? : String, children : Array[Html[M]]) -> Html[M]

fn[M] tr(style? : Array[String], id? : String, class? : String, children : Array[Html[M]]) -> Html[M]

fn[M] u(style? : Array[String], id? : String, class? : String, children : Array[Html[M]]) -> Html[M]

fn[M] ul(style? : Array[String], id? : String, class? : String, click? : M, children : Array[Html[M]]) -> Html[M]