Skip to content

Commit

Permalink
Impl View for Option<V> and create use_on_drop hook
Browse files Browse the repository at this point in the history
  • Loading branch information
matthunz committed Jan 26, 2024
1 parent 13ef693 commit 0418a87
Show file tree
Hide file tree
Showing 9 changed files with 93 additions and 23 deletions.
10 changes: 4 additions & 6 deletions crates/concoct-web/src/html.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use concoct::{
hook::{use_context, use_provider, use_ref},
IntoAction, View,
hook::{use_context, use_on_drop, use_provider, use_ref},
IntoAction, View,
};
use std::{borrow::Cow, cell::RefCell, rc::Rc};
use web_sys::{
Expand Down Expand Up @@ -133,15 +133,13 @@ where
let mut data_ref = data.borrow_mut();

let web_cx: Rc<WebContext> = use_context(cx);
let _data_clone = data.clone();
let data_clone = data.clone();

/*
use_on_drop(move || {
use_on_drop(cx, move || {
if let Some(element) = &data_clone.borrow_mut().element {
element.remove();
}
});
*/

if data_ref.element.is_none() {
let elem = web_cx.document.create_element(&self.tag).unwrap();
Expand Down
9 changes: 8 additions & 1 deletion crates/concoct-web/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use concoct::{
hook::{use_context, use_provider, use_ref}, view::TextContext, Scope, View
hook::{use_context, use_on_drop, use_provider, use_ref},
view::TextContext,
Scope, View,
};
use rustc_hash::FxHasher;
use std::{
Expand Down Expand Up @@ -54,6 +56,11 @@ impl<T: 'static, A: 'static, C: View<T, A>> View<T, A> for HtmlRoot<C> {
(Cell::new(hash), elem)
});

let node_clone = node.clone();
use_on_drop(cx, move || {
node_clone.remove();
});

if !is_init {
let mut hasher = FxHasher::default();
s.hash(&mut hasher);
Expand Down
3 changes: 3 additions & 0 deletions crates/concoct/src/hook/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
mod use_context;
pub use self::use_context::use_context;

mod use_on_drop;
pub use self::use_on_drop::use_on_drop;

mod use_provider;
pub use self::use_provider::use_provider;

Expand Down
17 changes: 17 additions & 0 deletions crates/concoct/src/hook/use_on_drop.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use super::use_ref;
use crate::Scope;

struct Dropper<F: FnOnce()> {
f: Option<F>,
}

impl<F: FnOnce()> Drop for Dropper<F> {
fn drop(&mut self) {
self.f.take().unwrap()()
}
}

/// Hook to call a funtion when this scope has dropped.
pub fn use_on_drop<T, A>(cx: &Scope<T, A>, on_drop: impl FnOnce() + 'static) {
use_ref(cx, || Dropper { f: Some(on_drop) });
}
35 changes: 20 additions & 15 deletions crates/concoct/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ impl<T, A> Handle<T, A> {
/// Scope of a view.
pub struct Scope<T, A = ()> {
key: DefaultKey,
parent: Option<DefaultKey>,
node: Node,
update: Rc<dyn Fn(Rc<dyn Fn(&mut T) -> Option<A>>)>,
is_empty: Cell<bool>,
Expand Down Expand Up @@ -99,6 +100,7 @@ fn build_inner<T, A>(view: &mut impl View<T, A>, cx: &Scope<T, A>) {

let child_cx = Scope {
key,
parent: Some(cx.key),
node,
update: cx.update.clone(),
is_empty: Cell::new(false),
Expand All @@ -114,21 +116,24 @@ fn build_inner<T, A>(view: &mut impl View<T, A>, cx: &Scope<T, A>) {

fn rebuild_inner<T, A>(view: &mut impl View<T, A>, cx: &Scope<T, A>) {
for child_key in &cx.node.inner.borrow().children {
let node = cx.nodes.borrow()[*child_key].clone();
node.inner.borrow_mut().hook_idx = 0;

let child_cx = Scope {
key: *child_key,
node,
update: cx.update.clone(),
is_empty: Cell::new(false),
nodes: cx.nodes.clone(),
contexts: cx.contexts.clone(),
};

let mut body = view.body(&child_cx);
if !child_cx.is_empty.get() {
rebuild_inner(&mut body, &child_cx);
let node = cx.nodes.borrow().get(*child_key).cloned();
if let Some(node) = node {
node.inner.borrow_mut().hook_idx = 0;

let child_cx = Scope {
key: *child_key,
node,
parent: Some(cx.key),
update: cx.update.clone(),
is_empty: Cell::new(false),
nodes: cx.nodes.clone(),
contexts: cx.contexts.clone(),
};

let mut body = view.body(&child_cx);
if !child_cx.is_empty.get() {
rebuild_inner(&mut body, &child_cx);
}
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions crates/concoct/src/vdom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ impl<T, V> VirtualDom<T, V> {
let cx = Scope {
key: root_key,
node,
parent: None,
update: Rc::new(move |f| {
let mut channel_ref = channel.borrow_mut();
channel_ref.updates.push(f);
Expand Down Expand Up @@ -89,6 +90,7 @@ impl<T, V> VirtualDom<T, V> {
let cx = Scope {
key: root_key,
node,
parent: None,
update: Rc::new(move |f| {
let mut channel_ref = channel.borrow_mut();
channel_ref.updates.push(f);
Expand Down
3 changes: 2 additions & 1 deletion crates/concoct/src/view/adapt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ where
F: FnMut(&mut T1, Rc<dyn Fn(&mut T2) -> Option<A2>>) -> Option<A1> + 'static,
V: View<T2, A2>,
{
fn body(&mut self, cx: &crate::Scope<T1, A1>) -> impl View<T1, A1> {
fn body(&mut self, cx: &Scope<T1, A1>) -> impl View<T1, A1> {
let parent_update = cx.update.clone();
let mapper = self.f.clone();
let update = Rc::new(move |f: Rc<dyn Fn(&mut T2) -> Option<A2>>| {
Expand All @@ -44,6 +44,7 @@ where
let child_cx = Scope {
key: cx.key,
node: cx.node.clone(),
parent: cx.parent,
update,
is_empty: cx.is_empty.clone(),
nodes: cx.nodes.clone(),
Expand Down
36 changes: 36 additions & 0 deletions crates/concoct/src/view/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! Viewable components.

use crate::hook::use_ref;
use crate::{build_inner, hook::use_context, rebuild_inner, Scope};
use std::mem;
use std::{cell::Cell, rc::Rc};

mod adapt;
Expand Down Expand Up @@ -30,6 +32,39 @@ impl<T, A, V: View<T, A>> View<T, A> for &mut V {
}
}

impl<T, A, V: View<T, A>> View<T, A> for Option<V> {
fn body(&mut self, cx: &Scope<T, A>) -> impl View<T, A> {
let is_some = use_ref(cx, || false);

if let Some(view) = self {
if *is_some {
rebuild_inner(view, cx);
} else {
build_inner(view, cx);
}
*is_some = true;
} else if *is_some {
*is_some = false;

let mut nodes_ref = cx.nodes.borrow_mut();

let mut stack = Vec::new();
for child_key in &mem::take(&mut cx.node.inner.borrow_mut().children) {
let child_node = nodes_ref[*child_key].clone();
stack.push((*child_key, child_node));
}

while let Some((key, node)) = stack.pop() {
nodes_ref.remove(key);
for child_key in &node.inner.borrow().children {
let child_node = nodes_ref[*child_key].clone();
stack.push((*child_key, child_node));
}
}
}
}
}

macro_rules! impl_view_for_tuple {
($($t:tt : $idx:tt),*) => {
impl<T, A, $($t: View<T, A>),*> View<T, A> for ($($t),*) {
Expand All @@ -45,6 +80,7 @@ macro_rules! impl_view_for_tuple {
let cx = Scope {
key,
node,
parent: Some(cx.key),
update: cx.update.clone(),
is_empty: Cell::new(false),
nodes: cx.nodes.clone(),
Expand Down
1 change: 1 addition & 0 deletions web_examples/counter/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ impl View<Counter> for Counter {
format!("High five count: {}", self.count),
html::button("Up high!").on_click(|state: &mut Self, _event| state.count += 1),
html::button("Down low!").on_click(|state: &mut Self, _event| state.count -= 1),

)
}
}
Expand Down

0 comments on commit 0418a87

Please sign in to comment.