Minimal prototype

This commit is contained in:
Denis Kolodin 2017-12-16 17:19:27 +03:00
commit 6a0067838a
4 changed files with 235 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/target/
**/*.rs.bk

54
Cargo.lock generated Normal file
View File

@ -0,0 +1,54 @@
[[package]]
name = "dtoa"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "itoa"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "num-traits"
version = "0.1.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde_json"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "stdweb"
version = "0.1.3"
source = "git+https://github.com/koute/stdweb#5eb5f0ff176464dbd30d41f31b53804a8bb2847b"
dependencies = [
"serde 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "yew-prototype"
version = "0.1.0"
dependencies = [
"stdweb 0.1.3 (git+https://github.com/koute/stdweb)",
]
[metadata]
"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
"checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c"
"checksum num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "cacfcab5eb48250ee7d0c7896b51a2c5eec99c1feea5f32025635f5ae4b00070"
"checksum serde 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)" = "1c57ab4ec5fa85d08aaf8ed9245899d9bbdd66768945b21113b84d5f595cb6a1"
"checksum serde_json 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7cf5b0b5b4bd22eeecb7e01ac2e1225c7ef5e4272b79ee28a8392a8c8489c839"
"checksum stdweb 0.1.3 (git+https://github.com/koute/stdweb)" = "<none>"

7
Cargo.toml Normal file
View File

@ -0,0 +1,7 @@
[package]
name = "yew-prototype"
version = "0.1.0"
authors = ["Denis Kolodin <deniskolodin@gmail.com>"]
[dependencies]
stdweb = { git = "https://github.com/koute/stdweb" }

171
src/main.rs Normal file
View File

@ -0,0 +1,171 @@
#[macro_use]
extern crate stdweb;
struct Model {
value: u8,
}
enum Msg {
Increment,
Decrement,
}
fn update(model: &mut Model, msg: Msg) {
match msg {
Msg::Increment => {
model.value = model.value + 1;
}
Msg::Decrement => {
model.value = model.value - 1;
}
}
}
fn view(model: &Model) -> html::Html<Msg> {
use html::*;
div(vec![
onclick(|_| Msg::Increment)
])
}
/*
html! {
<div onClick=(|_| { Msg::Increment })>{ model.x }>
}
*/
fn main() {
let model = Model {
value: 0,
};
html::program(model, update, view);
}
mod html {
use std::rc::Rc;
use std::cell::RefCell;
/*
pub trait Message {}
impl<T: ConcreteEvent> Fn(T) -> Self for Message {
}
*/
use stdweb;
use stdweb::web::{
INode,
IEventTarget,
Element,
document,
};
use stdweb::web::event::{
ClickEvent,
};
fn replace_body(element: Element) {
let body = document().query_selector("body").unwrap();
while body.has_child_nodes() {
body.remove_child(&body.last_child().unwrap()).unwrap();
}
body.append_child(&element);
}
pub fn program<M, MSG, U, V>(mut model: M, update: U, view: V)
where
M: 'static,
MSG: 'static,
U: Fn(&mut M, MSG) + 'static,
V: Fn(&M) -> Html<MSG> + 'static,
{
stdweb::initialize();
// No messages at start
let messages = Rc::new(RefCell::new(Vec::new()));
let mut callback = move || {
println!("Process messages");
let mut borrowed = messages.borrow_mut();
for msg in borrowed.drain(..) {
update(&mut model, msg);
}
let html = view(&model);
let element = html.render(messages.clone());
replace_body(element);
};
// Initial call for first rendering
callback();
js! {
let callback = @{callback};
window.yew_loop = function() {
callback();
}
};
stdweb::event_loop();
}
pub type Html<MSG> = Tag<MSG>;
pub trait Listener<MSG> {
fn attach(&mut self, element: &Element, messages: Messages<MSG>);
}
type Messages<MSG> = Rc<RefCell<Vec<MSG>>>;
type Listeners<MSG> = Vec<Box<Listener<MSG>>>;
pub struct Tag<MSG> {
tag: &'static str,
listeners: Listeners<MSG>,
}
impl<MSG> Tag<MSG> {
fn new(tag: &'static str, listeners: Listeners<MSG>) -> Self {
Tag { tag, listeners }
}
fn render(self, messages: Messages<MSG>) -> Element {
let element = document().create_element(self.tag);
for mut listener in self.listeners {
listener.attach(&element, messages.clone());
}
element
}
}
pub fn div<MSG>(listeners: Vec<Box<Listener<MSG>>>) -> Tag<MSG> {
Tag::new("div", listeners)
}
pub fn button<MSG>(listeners: Vec<Box<Listener<MSG>>>) -> Tag<MSG> {
Tag::new("button", listeners)
}
pub fn onclick<F, MSG>(handler: F) -> Box<Listener<MSG>>
where
MSG: 'static,
F: Fn(ClickEvent) -> MSG + 'static
{
Box::new(OnClick(Some(handler)))
}
struct OnClick<T>(Option<T>);
impl<T, MSG> Listener<MSG> for OnClick<T>
where
MSG: 'static,
T: Fn(ClickEvent) -> MSG + 'static,
{
fn attach(&mut self, element: &Element, messages: Messages<MSG>) {
let handler = self.0.take().unwrap();
let sender = move |event| {
println!("Clicked!");
let msg = handler(event);
messages.borrow_mut().push(msg);
js! {
yew_loop();
}
};
element.add_event_listener(sender);
}
}
}