functional Web UI framework for MoonBit
Dependencies
///|
using @html {div, h1, button}
///|
#cfg(target="js")
test {
struct Model {
count : Int
}
enum Msg {
Inc
Dec
}
let app = @rabbita.simple_cell(
model={ count: 0 },
update=(msg, model) => {
let { count } = model
match msg {
Inc => { count: count + 1 }
Dec => { count: count - 1 }
}
},
view=(dispatch, model) => {
div([
h1("\{model.count}"),
button(on_click=dispatch(Inc), "+"),
button(on_click=dispatch(Dec), "-"),
])
},
)
new(app).mount("main")
}///|
#cfg(target="js")
using @html {fragment, input, nothing, ul, li, p}
///|
#cfg(target="js")
using @list {type List, empty}
///|
/// The todo plan
#cfg(target="js")
fn plan(name : String) -> Cell {
struct Model {
value : String
items : Map[String, Bool]
}
enum Msg {
Add
Change(String)
Done(String)
}
@rabbita.simple_cell(
model={ value: "", items: {} },
update=(msg, model) => {
let { value, items } = model
match msg {
Add => { value: "", items: items..set(value, false) }
Done(key) => { ..model, items: items..set(key, true) }
Change(value) => { ..model, value, }
}
},
view=(dispatch, model) => {
let { value, items } = model
let items = items.map((todo, done) => {
let text_style = if done { "text-decoration: line-through" } else { "" }
li(style=[text_style], [
p(todo),
button(on_click=dispatch(Done(todo)), "done"),
])
})
div(style=["border: 1px solid black", "padding: 1em"], [
h1(name),
ul(items),
input(
input_type=Text,
value~,
on_change=s => dispatch(Change(s)),
nothing,
),
button(on_click=dispatch(Add), "add"),
])
},
)
}
///|
/// Main app
#cfg(target="js")
test {
struct Model {
plans : List[Cell]
}
enum Msg {
NewPlan
}
let app = @rabbita.simple_cell(
model={ plans: empty() },
update=(msg, model) => {
let id = model.plans.length()
match msg {
NewPlan => { plans: model.plans.add(plan("plan \{id}")) }
}
},
view=(dispatch, model) => {
fragment([
div(model.plans.map(x => x.view())),
button(on_click=dispatch(NewPlan), "new plan"),
])
},
)
@rabbita.new(app).mount("app")
}type Apptype Cell┌─────────────────┐
│ ▼
│ ┌──────────┐
│ │ user │
│ └────┬─────┘
│ │ msg,model
│ ▼
│ ┌────────────┐ msg,model
│ │ update() │◄────────────┐
│ └──┬───────┬─┘ │
│ none,model│ │ │
│ ▼ │ │
│ ┌────────┐ │cmd,model │
│ │ view() │ │ │
│ └────┬───┘ │ │
│ │ │ │
│ html│ ▼ │
│ │ ┌─────────┐ │
└──────────────┘ │ runtime │─────────┘
└─────────┘┌─────────────────┐
│ ▼
│ ┌──────────┐
│ │ user │
│ └────┬─────┘
│ │ msg,model
│ ▼
│ ┌────────────┐ msg,model
│ │ update() │◄────────────┐
│ └──┬───────┬─┘ │
│ none,model│ │ │
│ ▼ │ │
│ ┌────────┐ │cmd,model │
│ │ view() │ │ │
│ └────┬───┘ │ │
│ │ │ │
│ html│ ▼ │
│ │ ┌─────────┐ │
└──────────────┘ │ runtime │─────────┘
└─────────┘test "minimal static page" {
let app = @rabbita.static_cell(div("hello world"))
ignore(app)
// use `@rabbita.new(app).mount("id")` in client
}#warnings("-alert_unstable")
test "render html to string" {
enum Msg {}
struct Model {
todos : Array[String]
}
let page = @rabbita.simple_cell(
model={ todos: ["todo1", "todo2"] },
update=(_ : Msg, model) => model,
view=(_, model) => @html.ul(model.todos.map(x => @html.li(x))),
)
inspect(
@rabbita.render_to_string(page),
content=(
#|<ul><li>todo1</li><li>todo2</li></ul>
),
)
} ┌──────┐
│ user │◀─────────┐
└──────┘ │
│ msg, model │
▼ │
┌──────────┐ │
│ update() │ │ html
└──────────┘ │
│ model │
▼ │
┌────────┐ │
│ view() │─────────┘
└────────┘button(on_click=dispatch(MyMsg), "click me")test "counter" {
struct Model {
count : Int
}
enum Msg {
Click
}
let app = @rabbita.simple_cell(
model={ count: 0 },
update=(msg, model) => {
match msg {
Click => { count: model.count + 1 }
}
},
view=(dispatch, model) => {
div([
h1("You clicked \{model.count} times."),
button(on_click=dispatch(Click), "+"),
])
},
)
ignore(app) // use `new(app).mount("id")` in client
}functional Web UI framework for MoonBit
Dependencies