Tidying up the todomvc example
This commit is contained in:
parent
cfdf561394
commit
b4613dc77b
|
@ -74,6 +74,21 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn remove_todo(&self, todo: &Todo) {
|
||||||
|
// TODO make this more efficient ?
|
||||||
|
self.todo_list.retain(|x| x.id != todo.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_filter(&self) {
|
||||||
|
let hash = document().location().unwrap().hash().unwrap();
|
||||||
|
|
||||||
|
self.filter.set(match hash.as_str() {
|
||||||
|
"#/active" => Filter::Active,
|
||||||
|
"#/completed" => Filter::Completed,
|
||||||
|
_ => Filter::All,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fn deserialize() -> Self {
|
fn deserialize() -> Self {
|
||||||
window().local_storage().get("todos-rust-dominator").and_then(|state_json| {
|
window().local_storage().get("todos-rust-dominator").and_then(|state_json| {
|
||||||
serde_json::from_str(state_json.as_str()).ok()
|
serde_json::from_str(state_json.as_str()).ok()
|
||||||
|
@ -130,12 +145,16 @@ fn link(href: &str, t: &str) -> Dom {
|
||||||
|
|
||||||
fn filter_button(state: Rc<State>, kind: Filter) -> Dom {
|
fn filter_button(state: Rc<State>, kind: Filter) -> Dom {
|
||||||
html!("a", {
|
html!("a", {
|
||||||
class("selected", state.filter.signal().map(clone!(kind => move |filter| filter == kind)).dynamic());
|
class("selected", state.filter.signal()
|
||||||
|
.map(clone!(kind => move |filter| filter == kind))
|
||||||
|
.dynamic());
|
||||||
|
|
||||||
attribute("href", match kind {
|
attribute("href", match kind {
|
||||||
Filter::Active => "#/active",
|
Filter::Active => "#/active",
|
||||||
Filter::Completed => "#/completed",
|
Filter::Completed => "#/completed",
|
||||||
Filter::All => "#/",
|
Filter::All => "#/",
|
||||||
});
|
});
|
||||||
|
|
||||||
children(&mut [
|
children(&mut [
|
||||||
text(match kind {
|
text(match kind {
|
||||||
Filter::Active => "Active",
|
Filter::Active => "Active",
|
||||||
|
@ -150,24 +169,14 @@ fn filter_button(state: Rc<State>, kind: Filter) -> Dom {
|
||||||
fn main() {
|
fn main() {
|
||||||
let state = Rc::new(State::deserialize());
|
let state = Rc::new(State::deserialize());
|
||||||
|
|
||||||
|
state.update_filter();
|
||||||
fn update_filter(state: &Rc<State>) {
|
|
||||||
let hash = document().location().unwrap().hash().unwrap();
|
|
||||||
|
|
||||||
state.filter.set(match hash.as_str() {
|
|
||||||
"#/active" => Filter::Active,
|
|
||||||
"#/completed" => Filter::Completed,
|
|
||||||
_ => Filter::All,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
update_filter(&state);
|
|
||||||
|
|
||||||
window().add_event_listener(clone!(state => move |_: HashChangeEvent| {
|
window().add_event_listener(clone!(state => move |_: HashChangeEvent| {
|
||||||
update_filter(&state);
|
state.update_filter();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
// TODO this should be in stdweb
|
||||||
let body = document().query_selector("body").unwrap().unwrap();
|
let body = document().query_selector("body").unwrap().unwrap();
|
||||||
|
|
||||||
dominator::append_dom(&body,
|
dominator::append_dom(&body,
|
||||||
|
@ -233,7 +242,7 @@ fn main() {
|
||||||
|
|
||||||
property("checked", state.todo_list.signal_vec()
|
property("checked", state.todo_list.signal_vec()
|
||||||
.map_signal(|todo| todo.completed.signal())
|
.map_signal(|todo| todo.completed.signal())
|
||||||
// TODO .filter()
|
// TODO use .filter()
|
||||||
.filter_map(|completed| if !completed { Some(()) } else { None })
|
.filter_map(|completed| if !completed { Some(()) } else { None })
|
||||||
.len()
|
.len()
|
||||||
.map(|len| len != 0)
|
.map(|len| len != 0)
|
||||||
|
@ -249,38 +258,24 @@ fn main() {
|
||||||
state.serialize();
|
state.serialize();
|
||||||
}));
|
}));
|
||||||
}),
|
}),
|
||||||
|
|
||||||
html!("label", {
|
html!("label", {
|
||||||
attribute("for", "toggle-all");
|
attribute("for", "toggle-all");
|
||||||
children(&mut [
|
children(&mut [
|
||||||
text("Mark all as complete"),
|
text("Mark all as complete"),
|
||||||
]);
|
]);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
html!("ul", {
|
html!("ul", {
|
||||||
class("todo-list", true);
|
class("todo-list", true);
|
||||||
children(state.todo_list.signal_vec()/*.map_signal(clone!(state => move |todo| {
|
|
||||||
state.filter.signal().switch(clone!(todo => move |filter| {
|
|
||||||
// TODO figure out a way to avoid using Box
|
|
||||||
let filter: Box<Signal<Item = bool>> = match filter {
|
|
||||||
// TODO .not() method
|
|
||||||
Filter::Active => Box::new(todo.completed.signal().map(|completed| !completed)),
|
|
||||||
Filter::Completed => Box::new(todo.completed.signal()),
|
|
||||||
Filter::All => Box::new(always(true)),
|
|
||||||
};
|
|
||||||
|
|
||||||
filter
|
|
||||||
})).map_dedupe(move |show| {
|
|
||||||
if *show {
|
|
||||||
// TODO figure out a way to avoid this clone
|
|
||||||
Some(todo.clone())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})).filter_map(|todo| todo)*/.map(clone!(state => move |todo| {
|
|
||||||
console!(log, "CREATING", todo.title.get());
|
|
||||||
|
|
||||||
|
children(state.todo_list.signal_vec()
|
||||||
|
.map(clone!(state => move |todo| {
|
||||||
html!("li", {
|
html!("li", {
|
||||||
class("editing", todo.editing.signal().map(|x| x.is_some()).dynamic());
|
class("editing", todo.editing.signal()
|
||||||
|
.map(|x| x.is_some())
|
||||||
|
.dynamic());
|
||||||
|
|
||||||
class("completed", todo.completed.signal().dynamic());
|
class("completed", todo.completed.signal().dynamic());
|
||||||
|
|
||||||
property("hidden",
|
property("hidden",
|
||||||
|
@ -303,7 +298,9 @@ fn main() {
|
||||||
html!("input", {
|
html!("input", {
|
||||||
attribute("type", "checkbox");
|
attribute("type", "checkbox");
|
||||||
class("toggle", true);
|
class("toggle", true);
|
||||||
|
|
||||||
property("checked", todo.completed.signal().dynamic());
|
property("checked", todo.completed.signal().dynamic());
|
||||||
|
|
||||||
event(clone!(state, todo => move |event: ChangeEvent| {
|
event(clone!(state, todo => move |event: ChangeEvent| {
|
||||||
todo.completed.set(get_checked(&event));
|
todo.completed.set(get_checked(&event));
|
||||||
state.serialize();
|
state.serialize();
|
||||||
|
@ -311,20 +308,19 @@ fn main() {
|
||||||
}),
|
}),
|
||||||
|
|
||||||
html!("label", {
|
html!("label", {
|
||||||
children(&mut [
|
|
||||||
text(todo.title.signal().map(|x| { console!(log, &x); x }).dynamic()),
|
|
||||||
]);
|
|
||||||
event(clone!(todo => move |_: DoubleClickEvent| {
|
event(clone!(todo => move |_: DoubleClickEvent| {
|
||||||
todo.editing.set(Some(todo.title.get()));
|
todo.editing.set(Some(todo.title.get()));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
children(&mut [
|
||||||
|
text(todo.title.signal().dynamic()),
|
||||||
|
]);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
html!("button", {
|
html!("button", {
|
||||||
class("destroy", true);
|
class("destroy", true);
|
||||||
event(clone!(state, todo => move |_: ClickEvent| {
|
event(clone!(state, todo => move |_: ClickEvent| {
|
||||||
// TODO make this more efficient ?
|
state.remove_todo(&todo);
|
||||||
state.todo_list.retain(|x| x.id != todo.id);
|
|
||||||
|
|
||||||
state.serialize();
|
state.serialize();
|
||||||
}));
|
}));
|
||||||
}),
|
}),
|
||||||
|
@ -333,11 +329,19 @@ fn main() {
|
||||||
|
|
||||||
html!("input", {
|
html!("input", {
|
||||||
class("edit", true);
|
class("edit", true);
|
||||||
property("value", todo.editing.signal().map(|x| x.unwrap_or_else(|| "".to_owned())).dynamic());
|
|
||||||
property("hidden", todo.editing.signal().map(|x| x.is_none()).dynamic());
|
property("value", todo.editing.signal()
|
||||||
|
.map(|x| x.unwrap_or_else(|| "".to_owned()))
|
||||||
|
.dynamic());
|
||||||
|
|
||||||
|
property("hidden", todo.editing.signal()
|
||||||
|
.map(|x| x.is_none())
|
||||||
|
.dynamic());
|
||||||
|
|
||||||
// TODO dedupe this somehow ?
|
// TODO dedupe this somehow ?
|
||||||
focused(todo.editing.signal().map(|x| x.is_some()).dynamic());
|
focused(todo.editing.signal()
|
||||||
|
.map(|x| x.is_some())
|
||||||
|
.dynamic());
|
||||||
|
|
||||||
event(clone!(todo => move |event: KeyDownEvent| {
|
event(clone!(todo => move |event: KeyDownEvent| {
|
||||||
let key = event.key();
|
let key = event.key();
|
||||||
|
@ -361,8 +365,7 @@ fn main() {
|
||||||
todo.title.set(title);
|
todo.title.set(title);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// TODO make this more efficient ?
|
state.remove_todo(&todo);
|
||||||
state.todo_list.retain(|x| x.id != todo.id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
state.serialize();
|
state.serialize();
|
||||||
|
@ -371,7 +374,9 @@ fn main() {
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
})
|
})
|
||||||
})).dynamic());
|
}))
|
||||||
|
.dynamic()
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
}),
|
}),
|
||||||
|
@ -380,17 +385,21 @@ fn main() {
|
||||||
class("footer", true);
|
class("footer", true);
|
||||||
|
|
||||||
// Hide if it doesn't have any todos.
|
// Hide if it doesn't have any todos.
|
||||||
property("hidden", state.todo_list.signal_vec().len().map(|len| len == 0).dynamic());
|
property("hidden", state.todo_list.signal_vec()
|
||||||
|
.len()
|
||||||
|
.map(|len| len == 0)
|
||||||
|
.dynamic());
|
||||||
|
|
||||||
children(&mut [
|
children(&mut [
|
||||||
html!("span", {
|
html!("span", {
|
||||||
class("todo-count", true);
|
class("todo-count", true);
|
||||||
children(
|
|
||||||
state.todo_list.signal_vec()
|
children(state.todo_list.signal_vec()
|
||||||
.map_signal(|todo| todo.completed.signal())
|
.map_signal(|todo| todo.completed.signal())
|
||||||
// TODO .filter()
|
// TODO use .filter()
|
||||||
.filter_map(|completed| if !completed { Some(()) } else { None })
|
.filter_map(|completed| if !completed { Some(()) } else { None })
|
||||||
.len()
|
.len()
|
||||||
|
// TODO make this more efficient
|
||||||
.map(|len| {
|
.map(|len| {
|
||||||
vec![
|
vec![
|
||||||
simple("strong", &mut [
|
simple("strong", &mut [
|
||||||
|
@ -427,6 +436,7 @@ fn main() {
|
||||||
// Hide if it doesn't have any completed items.
|
// Hide if it doesn't have any completed items.
|
||||||
property("hidden", state.todo_list.signal_vec()
|
property("hidden", state.todo_list.signal_vec()
|
||||||
.map_signal(|todo| todo.completed.signal())
|
.map_signal(|todo| todo.completed.signal())
|
||||||
|
// TODO use .filter()
|
||||||
.filter_map(|completed| if completed { Some(()) } else { None })
|
.filter_map(|completed| if completed { Some(()) } else { None })
|
||||||
.len()
|
.len()
|
||||||
.map(|len| len == 0)
|
.map(|len| len == 0)
|
||||||
|
@ -434,7 +444,6 @@ fn main() {
|
||||||
|
|
||||||
event(clone!(state => move |_: ClickEvent| {
|
event(clone!(state => move |_: ClickEvent| {
|
||||||
state.todo_list.retain(|todo| todo.completed.get() == false);
|
state.todo_list.retain(|todo| todo.completed.get() == false);
|
||||||
|
|
||||||
state.serialize();
|
state.serialize();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue