extern crate serde_json; extern crate stdweb; #[macro_use] extern crate virtual_view; extern crate virtual_view_dom; use stdweb::web::{document, IEventTarget, INonElementParentNode, Node}; use stdweb::web::html_element::InputElement; use stdweb::unstable::TryInto; use virtual_view::{Array, Children, Component, EventManager, Instance, Prop, Props, Renderer, Updater, View}; use virtual_view_dom::{Handler, Patcher, TransactionEvent}; struct App; impl App { fn add_todo(updater: &Updater) -> Prop { updater.set_state(|prev| { let mut next = prev.clone(); let next_id = prev.get("next_id").number().unwrap(); next.update("todos", |todos| { if let Some(array) = todos.array_mut() { array.push(prop!({ "id": next_id, "completed": false, "text": prev.get("text"), })) } }); next.set("next_id", next_id + 1.0); next.remove("text"); next }); Prop::Null } fn text_change(updater: &Updater, e: &mut Props) -> Prop { let id = e.get("component_id").string().unwrap(); if let Some(node) = dom_node(id) { let input: InputElement = node.try_into().unwrap(); updater.set_state(move |prev| { let mut next = prev.clone(); next.set("text", input.raw_value()); next }); } Prop::Null } fn remove_todo(updater: &Updater, e: &mut Props) -> Prop { let id = e.take("todo_id").unwrap(); updater.set_state(move |prev| { let mut next = prev.clone(); next.update("todos", |todos| { if let Some(array) = todos.array_mut() { if let Some(index) = array.iter().position(|todo| { let todo = todo.object().unwrap(); todo.get("id") == &id }) { array.remove(index); } } }); next }); Prop::Null } } impl Component for App { fn initial_state(&self, _: &Props) -> Props { props! { "text": "Finish me!", "next_id": 1, "todos": [{"id": 0, "completed": false, "text": "Todo"}] } } fn render(&self, instance: &Instance, _: &Props, _: &Children) -> View { view! {
<{AddTodo} add_todo={ block { let updater = instance.updater.clone(); move |_: &mut Props| App::add_todo(&updater) } } /> <{VisibleTodoList} todos={ instance.state.get("todos") } remove_todo={ block { let updater = instance.updater.clone(); move |e: &mut Props| App::remove_todo(&updater, e) } } /> <{Footer}/>
} } } struct AddTodo; impl Component for AddTodo { fn render(&self, _: &Instance, props: &Props, _: &Children) -> View { view! { } } } struct VisibleTodoList; impl Component for VisibleTodoList { fn render(&self, _: &Instance, props: &Props, _: &Children) -> View { view! {
<{TodoList} todos={ props.get("todos") } remove_todo={ props.get("remove_todo") }/>
} } } struct Todo; impl Component for Todo { fn render(&self, _: &Instance, props: &Props, _: &Children) -> View { let id = props.take("id").unwrap(); let completed = props.get("completed").boolean().unwrap_or(false); let remove_todo = props.take("remove_todo").unwrap(); view! {
  • {props.get("text")}
  • } } } struct TodoList; impl Component for TodoList { fn render(&self, _: &Instance, props: &Props, _: &Children) -> View { let empty_array = Array::new(); let todos = props.get("todos").array().unwrap_or(&empty_array); view! { } } } struct Footer; impl Component for Footer { fn render(&self, _: &Instance, _: &Props, _: &Children) -> View { view! { } } } fn dom_node(id: &str) -> Option { unsafe { PATCHER.as_ref().unwrap().node(id) } } static mut PATCHER: Option = None; fn main() { stdweb::initialize(); let event_manager = EventManager::new(); let handler = Handler::new(document()); let patcher = Patcher::new( document().get_element_by_id("app").unwrap().into(), document(), event_manager.clone(), ); unsafe { PATCHER = Some(patcher); } document().add_event_listener::(|e| unsafe { PATCHER.as_mut().unwrap().patch(&e.transaction()); }); let _renderer = Renderer::new( view! { <{App}/> }, event_manager, handler, ); stdweb::event_loop(); }