respo 0.0.1

a tiny virtual DOM library migrated from ClojureScript
Documentation

Respo in Rust

Respo Crate

Docs(TODO) https://docs.rs/respo

Reimagine Respo.cljs in Rust.

Usage

A preview example:

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Store {
  pub counted: i32,
  pub tasks: Vec<Task>,
  pub states: StatesTree,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Task {
  pub id: String,
  pub done: bool,
  pub content: String,
  pub time: f32,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ActionOp {
  Increment,
  Decrement,
  StatesChange(Vec<String>, Option<Value>),
  AddTask(String, String),
  RemoveTask(String),
  UpdateTask(String, String),
  ToggleTask(String),
}

pub fn apply_action(store: &mut Store, op: ActionOp) -> Result<(), String> {
  match op {
    ActionOp::Increment => {
      store.counted += 1;
    }
    ActionOp::Decrement => {
      store.counted -= 1;
    }
    ActionOp::StatesChange(path, new_state) => {
      store.states = store.states.set_in(&path, new_state);
    }
    ActionOp::AddTask(id, content) => store.tasks.push(Task {
      id,
      content,
      time: 0.0,
      done: false,
    }),
    ActionOp::RemoveTask(id) => {
      store.tasks.retain(|task| task.id != id);
    }
    ActionOp::UpdateTask(id, content) => {
      let mut found = false;
      for task in &mut store.tasks {
        if task.id == id {
          task.content = content.to_owned();
          found = true;
        }
      }
      if !found {
        return Err(format!("task {} not found", id));
      }
    }
    ActionOp::ToggleTask(id) => {
      let mut found = false;
      for task in &mut store.tasks {
        if task.id == id {
          util::log!("change task {:?}", task);
          task.done = !task.done;
          found = true;
        }
      }
      if !found {
        return Err(format!("task {} not found", id));
      }
    }
  }
  Ok(())
}
#[wasm_bindgen(js_name = loadDemoApp)]
pub fn load_demo_app() -> JsValue {
  panic::set_hook(Box::new(console_error_panic_hook::hook));

  let mount_target = query_select_node(".app").expect("found mount target");

  // need to push store inside function to keep all in one thread
  let global_store = Rc::new(RefCell::new(Store {
    counted: 0,
    states: StatesTree::default(),
    tasks: vec![],
  }));

  let store_to_action = global_store.clone();
  let dispatch_action = move |op: ActionOp| -> Result<(), String> {
    // util::log!("action {:?} store, {:?}", op, store_to_action.borrow());
    let mut store = store_to_action.borrow_mut();
    apply_action(&mut store, op)?;

    // util::log!("store after action {:?}", store);
    Ok(())
  };

  render_node(
    mount_target,
    Box::new(move || -> Result<RespoNode<ActionOp>, String> {
      let store = global_store.borrow();
      let states = store.states.clone();

      // util::log!("global store: {:?}", store);

      Ok(
        div()
          .class(ui_global())
          .add_style(RespoStyle::default().padding(12.0).to_owned())
          .add_children([
            comp_counter(&states.pick("counter"), store.counted),
            comp_panel(&states.pick("panel"))?,
            comp_todolist(&states.pick("todolist"), &store.tasks)?,
          ])
          .to_owned(),
      )
    }),
    DispatchFn(Rc::new(dispatch_action)),
  )
  .expect("rendering node");

  JsValue::NULL
}

License

Apache License 2.0 .