Skip to content

Commit

Permalink
Fixed a bug with rendering multiple Els from top level. Fixed typo wi…
Browse files Browse the repository at this point in the history
…th viewBox attr
  • Loading branch information
David-OConnor committed May 28, 2019
1 parent a99285b commit 36e8071
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 57 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## v0.3.5
- Fixed a bug where view functions returning `Vec<El>` weren't rendering properly
- Fixed a type with the `viewBox` attribute

## v0.3.4
- The `update` fn now accepts a (new) `Orders` struct, and returns nothing. Renders occur implicitly,
with the option to skip rendering, update with an additional message, or perform an asynchronous
Expand Down
16 changes: 8 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "seed"
version = "0.3.4"
version = "0.3.5"
description = "A Rust framework for creating web apps, using WebAssembly"
authors = ["DavidOConnor <david.alan.oconnor@gmail.com>"]
license = "MIT"
Expand Down
3 changes: 2 additions & 1 deletion examples/counter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ fn success_level(clicks: i32) -> El<Msg> {
p![descrip]
}

use wasm_bindgen::JsCast;

/// The top-level component we pass to the virtual dom. Must accept the model as its
/// only argument, and output has to implement trait ElContainer.
fn view(model: &Model) -> El<Msg> {
Expand Down Expand Up @@ -84,7 +86,6 @@ fn view(model: &Model) -> El<Msg> {
h2![
style! {"padding" => 50},
"Nice!",
// did_mount(|_| log!("This shows when clicks reach 10")).update2(Msg::ChangeWWC("Did mount".into())),
did_mount(|_| log!("This shows when clicks reach 10")),
will_unmount(|_| log!("This shows when clicks drop below 10")),
]
Expand Down
3 changes: 2 additions & 1 deletion examples/mathjax/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
An example using Mathjax, for LaTeX-like math notation. Doesn't demonstrate Seed-specific functionality,
but provides a syntax example.

# [Linear algebra cheatsheet](https://github.com/David-OConnor/linalg)
# [Linear algebra cheatsheet](https://github.com/David-OConnor/linalg)
# [Hosted](https://david-oconnor.github.io/linalg/)
16 changes: 15 additions & 1 deletion src/dom_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -588,7 +588,7 @@ make_attrs! {
CalcMode => "calcMode", CapHeight => "cap-height", Clip => "clip",
// todo fill in rest from link above.

Path => "path", D => "d", Xmlns => "xmlns", ViewBox => "ViewBox", Fill => "fill"
Path => "path", D => "d", Xmlns => "xmlns", ViewBox => "viewBox", Fill => "fill"
}

/// Similar to tag population.
Expand Down Expand Up @@ -1130,6 +1130,8 @@ pub struct El<Ms: 'static> {
// todo temp?
// pub key: Option<u32>,
pub namespace: Option<Namespace>,
// A unique identifier in the vdom. Useful for triggering one-off events.
pub ref_: Option<String>,

// control means we keep its text input (value) in sync with the model.
// static: bool
Expand Down Expand Up @@ -1200,6 +1202,8 @@ impl<Ms> El<Ms> {
hooks: LifecycleHooks::new(),
empty: false,
optimizations: Vec::new(),

ref_: None,
}
}

Expand Down Expand Up @@ -1236,6 +1240,8 @@ impl<Ms> El<Ms> {
// hooks: self.hooks, // todo fix
empty: self.empty,
optimizations: self.optimizations,

ref_: None,
}
}

Expand Down Expand Up @@ -1369,6 +1375,11 @@ impl<Ms> El<Ms> {

walk_tree_mut_inner(self, &mut f);
}

/// Set the ref
pub fn ref_<S: ToString>(&mut self, ref_: S) {
self.ref_ = Some(ref_.to_string());
}
}

/// Allow the user to clone their Els. Note that there's no easy way to clone the
Expand All @@ -1388,6 +1399,8 @@ impl<Ms> Clone for El<Ms> {
hooks: LifecycleHooks::new(),
empty: self.empty,
optimizations: self.optimizations.clone(),

ref_: self.ref_.clone(),
}
}
}
Expand All @@ -1403,6 +1416,7 @@ impl<Ms> PartialEq for El<Ms> {
&& self.listeners == other.listeners
&& self.namespace == other.namespace
&& self.empty == other.empty
&& self.ref_ == other.ref_
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pub use crate::{
fetch::{spawn_local, Method, Request},
routing::{push_route, Url},
util::{body, document, error, history, log, update, window},
vdom::App,
vdom::{find_el, App},
websys_bridge::{to_html_el, to_input, to_kbevent, to_mouse_event, to_select, to_textarea},
};
use wasm_bindgen::{closure::Closure, JsCast};
Expand Down
101 changes: 57 additions & 44 deletions src/vdom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ impl<Ms, Mdl, ElC: ElContainer<Ms> + 'static> App<Ms, Mdl, ElC> {
// We'll get a runtime panic if any are left un-removed.
detach_listeners(&mut old);

// todo copied from children loop in patch fn (DRY)
// todo much of the code below is copied from the patch fn (DRY)
let num_children_in_both = old.children.len().min(new.children.len());
let mut old_children_iter = old.children.into_iter();
let mut new_children_iter = new.children.iter_mut();
Expand Down Expand Up @@ -462,6 +462,32 @@ impl<Ms, Mdl, ElC: ElContainer<Ms> + 'static> App<Ms, Mdl, ElC> {
);
}

for child_new in new_children_iter {
// We ran out of old children to patch; create new ones.
setup_websys_el_and_children(&self.cfg.document, child_new);
websys_bridge::attach_el_and_children(child_new, &self.cfg.mount_point, &self.clone());
attach_listeners(child_new, &self.mailbox());
}

// Now purge any existing no-longer-needed children; they're not part of the new vdom.
// while let Some(mut child) = old_children_iter.next() {
for mut child in old_children_iter {
let child_el_ws = child.el_ws.take().expect("Missing child el_ws");

// TODO: DRY here between this and earlier in func
if let Some(unmount_actions) = &mut child.hooks.will_unmount {
(unmount_actions.actions)(&child_el_ws);
}

// todo get to the bottom of this: Ie why we need this code sometimes when using raw html elements.
match self.cfg.mount_point.remove_child(&child_el_ws) {
Ok(_) => {}
Err(_) => {
crate::log("Minor error patching html element. (remove)");
}
}
}

// Now that we've re-rendered, replace our stored El with the new one;
// it will be used as the old El next time.
self.data.main_el_vdom.borrow_mut().replace(new);
Expand All @@ -477,6 +503,20 @@ impl<Ms, Mdl, ElC: ElContainer<Ms> + 'static> App<Ms, Mdl, ElC> {
.push(Box::new(listener));
}

fn find(&self, ref_: &str) -> Option<El<Ms>> {
// todo expensive? We're cloning the whole vdom tree.
// todo: Let's iterate through refs instead, once this is working.

let top_el = &self
.data
.main_el_vdom
.borrow()
.clone()
.expect("Can't find main vdom el in find");

find_el(ref_, &top_el)
}

fn mailbox(&self) -> Mailbox<Ms> {
Mailbox::new(enclose!((self => s) move |message| {
s.update(message);
Expand Down Expand Up @@ -776,49 +816,6 @@ pub(crate) fn patch<'a, Ms, Mdl, ElC: ElContainer<Ms>>(
attach_listeners(child_new, &mailbox);
}

// // Now pair up children as best we can.
// // If there are the same number of children, assume there's a 1-to-1 mapping,
// // where we will not add or remove any; but patch as needed.
// let avail_old_children = &mut old.children;
// let mut prev_child: Option<web_sys::Node> = None;
// let mut best_match;
//// let mut t;
// for (i_new, child_new) in new.children.iter_mut().enumerate() {
// if avail_old_children.is_empty() {
// // One or more new children has been added, or much content has
// // changed, or we've made a mistake: Attach new children.
// websys_bridge::attach_els(child_new, &old_el_ws);
// let mut child_new = child_new;
// attach_listeners(&mut child_new, &mailbox);
//
// } else {
// // We still have old children to pick a match from. If we pick
// // incorrectly, or there is no "good" match, we'll have some
// // patching and/or attaching (rendering) to do in subsequent recursions.
// let mut scores: Vec<(u32, f32)> = avail_old_children
// .iter()
// .enumerate()
// .map(|(i_old, c_old)| (c_old.id.unwrap(), match_score(c_old, i_old, child_new, i_new)))
// .collect();
//
// // should put highest score at the end.
// scores.sort_by(|b, a| b.1.partial_cmp(&a.1).unwrap());
//
// // Sorting children vice picking the best one makes this easier to handle
// // without irking the borrow checker, despite appearing less counter-intuitive,
// // due to the convenient pop method.
// avail_old_children.sort_by(|b, a| {
// scores
// .iter()
// .find(|s| s.0 == b.id.unwrap())
// .unwrap()
// .1
// .partial_cmp(&scores.iter().find(|s| s.0 == a.id.unwrap()).unwrap().1)
// .unwrap()
// });
//
// best_match = avail_old_children.pop().expect("Problem popping");

// Now purge any existing no-longer-needed children; they're not part of the new vdom.
// while let Some(mut child) = old_children_iter.next() {
for mut child in old_children_iter {
Expand Down Expand Up @@ -892,6 +889,22 @@ pub trait DomElLifecycle {
fn will_unmount(self) -> Option<Box<FnMut(&Element)>>;
}

/// Find the first element that matches the ref specified.
//pub fn find_el<'a, Msg>(ref_: &str, top_el: &'a El<Msg>) -> Option<&'a El<Msg>> {
pub fn find_el<Msg>(ref_: &str, top_el: &El<Msg>) -> Option<El<Msg>> {
if top_el.ref_ == Some(ref_.to_string()) {
return Some(top_el.clone());
}

for child in &top_el.children {
let result = find_el(ref_, child);
if result.is_some() {
return result;
}
}
return None;
}

#[cfg(test)]
pub mod tests {
use wasm_bindgen_test::*;
Expand Down

0 comments on commit 36e8071

Please # to comment.