Add component and parent access to scope (#1151)

* Add reference to parent to scope

* Add untyped scope
This commit is contained in:
Justin Starry 2020-04-28 20:33:50 +08:00 committed by GitHub
parent 404ff77ffb
commit 9c0951513d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 154 additions and 83 deletions

View File

@ -89,7 +89,7 @@ where
{ {
/// Creates a new `App` with a component in a context. /// Creates a new `App` with a component in a context.
pub fn new() -> Self { pub fn new() -> Self {
let scope = Scope::new(); let scope = Scope::new(None);
App { scope } App { scope }
} }

View File

@ -7,8 +7,8 @@ mod listener;
mod scope; mod scope;
pub use listener::*; pub use listener::*;
pub use scope::Scope; pub(crate) use scope::ComponentUpdate;
pub(crate) use scope::{AnyScope, ComponentUpdate}; pub use scope::{AnyScope, Scope};
use crate::callback::Callback; use crate::callback::Callback;
use crate::virtual_dom::{VChild, VList, VNode}; use crate::virtual_dom::{VChild, VList, VNode};

View File

@ -2,9 +2,10 @@ use super::{Callback, Component, NodeRef, Renderable};
use crate::scheduler::{scheduler, ComponentRunnableType, Runnable, Shared}; use crate::scheduler::{scheduler, ComponentRunnableType, Runnable, Shared};
use crate::virtual_dom::{VDiff, VNode}; use crate::virtual_dom::{VDiff, VNode};
use cfg_if::cfg_if; use cfg_if::cfg_if;
use std::any::Any; use std::any::{Any, TypeId};
use std::cell::RefCell; use std::cell::{Ref, RefCell};
use std::fmt; use std::fmt;
use std::ops::Deref;
use std::rc::Rc; use std::rc::Rc;
cfg_if! { cfg_if! {
if #[cfg(feature = "std_web")] { if #[cfg(feature = "std_web")] {
@ -24,9 +25,62 @@ pub(crate) enum ComponentUpdate<COMP: Component> {
Properties(COMP::Properties, NodeRef), Properties(COMP::Properties, NodeRef),
} }
/// Untyped scope used for accessing parent scope
#[derive(Debug, Clone)]
pub struct AnyScope {
type_id: TypeId,
parent: Option<Rc<AnyScope>>,
state: Rc<dyn Any>,
}
impl Default for AnyScope {
fn default() -> Self {
Self {
type_id: TypeId::of::<()>(),
parent: None,
state: Rc::new(()),
}
}
}
impl<COMP: Component> From<Scope<COMP>> for AnyScope {
fn from(scope: Scope<COMP>) -> Self {
AnyScope {
type_id: TypeId::of::<COMP>(),
parent: scope.parent,
state: Rc::new(scope.state),
}
}
}
impl AnyScope {
/// Returns the parent scope
pub fn get_parent(&self) -> Option<&AnyScope> {
self.parent.as_deref()
}
/// Returns the type of the linked component
pub fn get_type_id(&self) -> &TypeId {
&self.type_id
}
/// Attempts to downcast into a typed scope
pub fn downcast<COMP: Component>(self) -> Scope<COMP> {
Scope {
parent: self.parent,
state: self
.state
.downcast_ref::<Shared<ComponentState<COMP>>>()
.expect("unexpected component type")
.clone(),
}
}
}
/// A context which allows sending messages to a component. /// A context which allows sending messages to a component.
pub struct Scope<COMP: Component> { pub struct Scope<COMP: Component> {
shared_state: Shared<ComponentState<COMP>>, parent: Option<Rc<AnyScope>>,
state: Shared<ComponentState<COMP>>,
} }
impl<COMP: Component> fmt::Debug for Scope<COMP> { impl<COMP: Component> fmt::Debug for Scope<COMP> {
@ -38,22 +92,30 @@ impl<COMP: Component> fmt::Debug for Scope<COMP> {
impl<COMP: Component> Clone for Scope<COMP> { impl<COMP: Component> Clone for Scope<COMP> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Scope { Scope {
shared_state: self.shared_state.clone(), parent: self.parent.clone(),
state: self.state.clone(),
} }
} }
} }
impl<COMP: Component> Default for Scope<COMP> {
fn default() -> Self {
Scope::new()
}
}
impl<COMP: Component> Scope<COMP> { impl<COMP: Component> Scope<COMP> {
/// visible for testing /// Returns the parent scope
pub fn new() -> Self { pub fn get_parent(&self) -> Option<&AnyScope> {
let shared_state = Rc::new(RefCell::new(ComponentState::Empty)); self.parent.as_deref()
Scope { shared_state } }
/// Returns the linked component if available
pub fn get_component(&self) -> Option<impl Deref<Target = COMP> + '_> {
self.state.try_borrow().ok().and_then(|state_ref| {
state_ref.component()?;
Some(Ref::map(state_ref, |this| this.component().unwrap()))
})
}
pub(crate) fn new(parent: Option<AnyScope>) -> Self {
let parent = parent.map(Rc::new);
let state = Rc::new(RefCell::new(ComponentState::Empty));
Scope { parent, state }
} }
/// Mounts a component with `props` to the specified `element` in the DOM. /// Mounts a component with `props` to the specified `element` in the DOM.
@ -72,15 +134,15 @@ impl<COMP: Component> Scope<COMP> {
props, props,
ancestor, ancestor,
}; };
*scope.shared_state.borrow_mut() = ComponentState::Ready(ready_state); *scope.state.borrow_mut() = ComponentState::Ready(ready_state);
scope.create(); scope.create();
scope scope
} }
/// Schedules a task to create and render a component and then mount it to the DOM /// Schedules a task to create and render a component and then mount it to the DOM
pub(crate) fn create(&mut self) { pub(crate) fn create(&mut self) {
let shared_state = self.shared_state.clone(); let state = self.state.clone();
let create = CreateComponent { shared_state }; let create = CreateComponent { state };
scheduler().push_comp(ComponentRunnableType::Create, Box::new(create)); scheduler().push_comp(ComponentRunnableType::Create, Box::new(create));
self.rendered(true); self.rendered(true);
} }
@ -88,7 +150,7 @@ impl<COMP: Component> Scope<COMP> {
/// Schedules a task to send a message or new props to a component /// Schedules a task to send a message or new props to a component
pub(crate) fn update(&self, update: ComponentUpdate<COMP>) { pub(crate) fn update(&self, update: ComponentUpdate<COMP>) {
let update = UpdateComponent { let update = UpdateComponent {
shared_state: self.shared_state.clone(), state: self.state.clone(),
update, update,
}; };
scheduler().push_comp(ComponentRunnableType::Update, Box::new(update)); scheduler().push_comp(ComponentRunnableType::Update, Box::new(update));
@ -97,9 +159,9 @@ impl<COMP: Component> Scope<COMP> {
/// Schedules a task to call the rendered method on a component /// Schedules a task to call the rendered method on a component
pub(crate) fn rendered(&self, first_render: bool) { pub(crate) fn rendered(&self, first_render: bool) {
let shared_state = self.shared_state.clone(); let state = self.state.clone();
let rendered = RenderedComponent { let rendered = RenderedComponent {
shared_state, state,
first_render, first_render,
}; };
scheduler().push_comp(ComponentRunnableType::Rendered, Box::new(rendered)); scheduler().push_comp(ComponentRunnableType::Rendered, Box::new(rendered));
@ -107,8 +169,8 @@ impl<COMP: Component> Scope<COMP> {
/// Schedules a task to destroy a component /// Schedules a task to destroy a component
pub(crate) fn destroy(&mut self) { pub(crate) fn destroy(&mut self) {
let shared_state = self.shared_state.clone(); let state = self.state.clone();
let destroy = DestroyComponent { shared_state }; let destroy = DestroyComponent { state };
scheduler().push_comp(ComponentRunnableType::Destroy, Box::new(destroy)); scheduler().push_comp(ComponentRunnableType::Destroy, Box::new(destroy));
} }
@ -180,6 +242,15 @@ enum ComponentState<COMP: Component> {
Destroyed, Destroyed,
} }
impl<COMP: Component> ComponentState<COMP> {
fn component(&self) -> Option<&COMP> {
match self {
ComponentState::Created(state) => Some(&state.component),
_ => None,
}
}
}
impl<COMP: Component> fmt::Display for ComponentState<COMP> { impl<COMP: Component> fmt::Display for ComponentState<COMP> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match self { let name = match self {
@ -205,10 +276,11 @@ impl<COMP: Component> ReadyState<COMP> {
fn create(self) -> CreatedState<COMP> { fn create(self) -> CreatedState<COMP> {
CreatedState { CreatedState {
rendered: false, rendered: false,
component: COMP::create(self.props, self.scope), component: COMP::create(self.props, self.scope.clone()),
element: self.element, element: self.element,
last_frame: self.ancestor, last_frame: self.ancestor,
node_ref: self.node_ref, node_ref: self.node_ref,
scope: self.scope,
} }
} }
} }
@ -219,6 +291,7 @@ struct CreatedState<COMP: Component> {
component: COMP, component: COMP,
last_frame: Option<VNode>, last_frame: Option<VNode>,
node_ref: NodeRef, node_ref: NodeRef,
scope: Scope<COMP>,
} }
impl<COMP: Component> CreatedState<COMP> { impl<COMP: Component> CreatedState<COMP> {
@ -231,7 +304,12 @@ impl<COMP: Component> CreatedState<COMP> {
fn update(mut self) -> Self { fn update(mut self) -> Self {
let mut root = self.component.render(); let mut root = self.component.render();
if let Some(node) = root.apply(&self.element, None, self.last_frame) { if let Some(node) = root.apply(
&self.scope.clone().into(),
&self.element,
None,
self.last_frame,
) {
self.node_ref.set(Some(node)); self.node_ref.set(Some(node));
} else if let VNode::VComp(child) = &root { } else if let VNode::VComp(child) = &root {
// If the root VNode is a VComp, we won't have access to the rendered DOM node // If the root VNode is a VComp, we won't have access to the rendered DOM node
@ -249,7 +327,7 @@ struct RenderedComponent<COMP>
where where
COMP: Component, COMP: Component,
{ {
shared_state: Shared<ComponentState<COMP>>, state: Shared<ComponentState<COMP>>,
first_render: bool, first_render: bool,
} }
@ -258,8 +336,8 @@ where
COMP: Component, COMP: Component,
{ {
fn run(self: Box<Self>) { fn run(self: Box<Self>) {
let current_state = self.shared_state.replace(ComponentState::Processing); let current_state = self.state.replace(ComponentState::Processing);
self.shared_state.replace(match current_state { self.state.replace(match current_state {
ComponentState::Created(s) if !s.rendered => { ComponentState::Created(s) if !s.rendered => {
ComponentState::Created(s.rendered(self.first_render)) ComponentState::Created(s.rendered(self.first_render))
} }
@ -275,7 +353,7 @@ struct CreateComponent<COMP>
where where
COMP: Component, COMP: Component,
{ {
shared_state: Shared<ComponentState<COMP>>, state: Shared<ComponentState<COMP>>,
} }
impl<COMP> Runnable for CreateComponent<COMP> impl<COMP> Runnable for CreateComponent<COMP>
@ -283,8 +361,8 @@ where
COMP: Component, COMP: Component,
{ {
fn run(self: Box<Self>) { fn run(self: Box<Self>) {
let current_state = self.shared_state.replace(ComponentState::Processing); let current_state = self.state.replace(ComponentState::Processing);
self.shared_state.replace(match current_state { self.state.replace(match current_state {
ComponentState::Ready(s) => ComponentState::Created(s.create().update()), ComponentState::Ready(s) => ComponentState::Created(s.create().update()),
ComponentState::Created(_) | ComponentState::Destroyed => current_state, ComponentState::Created(_) | ComponentState::Destroyed => current_state,
ComponentState::Empty | ComponentState::Processing => { ComponentState::Empty | ComponentState::Processing => {
@ -298,7 +376,7 @@ struct DestroyComponent<COMP>
where where
COMP: Component, COMP: Component,
{ {
shared_state: Shared<ComponentState<COMP>>, state: Shared<ComponentState<COMP>>,
} }
impl<COMP> Runnable for DestroyComponent<COMP> impl<COMP> Runnable for DestroyComponent<COMP>
@ -306,7 +384,7 @@ where
COMP: Component, COMP: Component,
{ {
fn run(self: Box<Self>) { fn run(self: Box<Self>) {
match self.shared_state.replace(ComponentState::Destroyed) { match self.state.replace(ComponentState::Destroyed) {
ComponentState::Created(mut this) => { ComponentState::Created(mut this) => {
this.component.destroy(); this.component.destroy();
if let Some(last_frame) = &mut this.last_frame { if let Some(last_frame) = &mut this.last_frame {
@ -328,7 +406,7 @@ struct UpdateComponent<COMP>
where where
COMP: Component, COMP: Component,
{ {
shared_state: Shared<ComponentState<COMP>>, state: Shared<ComponentState<COMP>>,
update: ComponentUpdate<COMP>, update: ComponentUpdate<COMP>,
} }
@ -337,8 +415,8 @@ where
COMP: Component, COMP: Component,
{ {
fn run(self: Box<Self>) { fn run(self: Box<Self>) {
let current_state = self.shared_state.replace(ComponentState::Processing); let current_state = self.state.replace(ComponentState::Processing);
self.shared_state.replace(match current_state { self.state.replace(match current_state {
ComponentState::Created(mut this) => { ComponentState::Created(mut this) => {
let should_update = match self.update { let should_update = match self.update {
ComponentUpdate::Message(message) => this.component.update(message), ComponentUpdate::Message(message) => this.component.update(message),
@ -367,24 +445,3 @@ where
}); });
} }
} }
pub(crate) struct AnyScope {
scope: Box<dyn Any>,
}
impl<COMP: Component> From<Scope<COMP>> for AnyScope {
fn from(scope: Scope<COMP>) -> Self {
AnyScope {
scope: Box::new(scope),
}
}
}
impl AnyScope {
pub(crate) fn downcast<COMP: Component>(&self) -> Scope<COMP> {
self.scope
.downcast_ref::<Scope<COMP>>()
.expect("INTERNAL: unexpected component type, please report")
.clone()
}
}

View File

@ -11,6 +11,7 @@ pub mod vtag;
#[doc(hidden)] #[doc(hidden)]
pub mod vtext; pub mod vtext;
use crate::html::AnyScope;
use cfg_if::cfg_if; use cfg_if::cfg_if;
use indexmap::set::IndexSet; use indexmap::set::IndexSet;
use std::collections::HashMap; use std::collections::HashMap;
@ -217,6 +218,7 @@ pub(crate) trait VDiff {
/// (always removes the `Node` that exists). /// (always removes the `Node` that exists).
fn apply( fn apply(
&mut self, &mut self,
scope: &AnyScope,
parent: &Element, parent: &Element,
previous_sibling: Option<&Node>, previous_sibling: Option<&Node>,
ancestor: Option<VNode>, ancestor: Option<VNode>,

View File

@ -21,7 +21,7 @@ type Generator = dyn Fn(GeneratorType) -> Mounted;
/// Components can be generated by mounting or by overwriting an old component. /// Components can be generated by mounting or by overwriting an old component.
enum GeneratorType { enum GeneratorType {
Mount(Element, TextNode), Mount(AnyScope, Element, TextNode),
Overwrite(AnyScope), Overwrite(AnyScope),
} }
@ -111,8 +111,8 @@ impl VComp {
let node_ref_clone = node_ref.clone(); let node_ref_clone = node_ref.clone();
let generator = move |generator_type: GeneratorType| -> Mounted { let generator = move |generator_type: GeneratorType| -> Mounted {
match generator_type { match generator_type {
GeneratorType::Mount(element, dummy_node) => { GeneratorType::Mount(parent_scope, element, dummy_node) => {
let scope: Scope<COMP> = Scope::new(); let scope: Scope<COMP> = Scope::new(Some(parent_scope));
let mut scope = scope.mount_in_place( let mut scope = scope.mount_in_place(
element, element,
@ -156,8 +156,8 @@ impl VComp {
impl Unmounted { impl Unmounted {
/// Mount a virtual component using a generator. /// Mount a virtual component using a generator.
fn mount(self, parent: Element, dummy_node: TextNode) -> Mounted { fn mount(self, parent_scope: AnyScope, parent: Element, dummy_node: TextNode) -> Mounted {
(self.generator)(GeneratorType::Mount(parent, dummy_node)) (self.generator)(GeneratorType::Mount(parent_scope, parent, dummy_node))
} }
/// Overwrite an existing virtual component using a generator. /// Overwrite an existing virtual component using a generator.
@ -186,6 +186,7 @@ impl VDiff for VComp {
fn apply( fn apply(
&mut self, &mut self,
scope: &AnyScope,
parent: &Element, parent: &Element,
previous_sibling: Option<&Node>, previous_sibling: Option<&Node>,
ancestor: Option<VNode>, ancestor: Option<VNode>,
@ -246,7 +247,7 @@ impl VDiff for VComp {
result.expect("can't append node to parent"); result.expect("can't append node to parent");
} }
} }
this.mount(parent.to_owned(), dummy_node) this.mount(scope.clone(), parent.to_owned(), dummy_node)
} }
}; };

View File

@ -1,5 +1,6 @@
//! This module contains fragments implementation. //! This module contains fragments implementation.
use super::{VDiff, VNode, VText}; use super::{VDiff, VNode, VText};
use crate::html::AnyScope;
use cfg_if::cfg_if; use cfg_if::cfg_if;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
@ -77,6 +78,7 @@ impl VDiff for VList {
fn apply( fn apply(
&mut self, &mut self,
scope: &AnyScope,
parent: &Element, parent: &Element,
previous_sibling: Option<&Node>, previous_sibling: Option<&Node>,
ancestor: Option<VNode>, ancestor: Option<VNode>,
@ -143,20 +145,22 @@ impl VDiff for VList {
match rights_lookup.remove(key) { match rights_lookup.remove(key) {
Some(right) => { Some(right) => {
previous_sibling = previous_sibling =
left.apply(parent, previous_sibling.as_ref(), Some(right)); left.apply(scope, parent, previous_sibling.as_ref(), Some(right));
} }
None => { None => {
previous_sibling = left.apply(parent, previous_sibling.as_ref(), None); previous_sibling =
left.apply(scope, parent, previous_sibling.as_ref(), None);
} }
} }
} else { } else {
match rights.next() { match rights.next() {
Some(right) => { Some(right) => {
previous_sibling = previous_sibling =
left.apply(parent, previous_sibling.as_ref(), Some(right)); left.apply(scope, parent, previous_sibling.as_ref(), Some(right));
} }
None => { None => {
previous_sibling = left.apply(parent, previous_sibling.as_ref(), None); previous_sibling =
left.apply(scope, parent, previous_sibling.as_ref(), None);
} }
} }
} }

View File

@ -1,7 +1,7 @@
//! This module contains the implementation of abstract virtual node. //! This module contains the implementation of abstract virtual node.
use super::{VChild, VComp, VDiff, VList, VTag, VText}; use super::{VChild, VComp, VDiff, VList, VTag, VText};
use crate::html::{Component, Renderable}; use crate::html::{AnyScope, Component, Renderable};
use cfg_if::cfg_if; use cfg_if::cfg_if;
use log::warn; use log::warn;
use std::cmp::PartialEq; use std::cmp::PartialEq;
@ -63,15 +63,16 @@ impl VDiff for VNode {
fn apply( fn apply(
&mut self, &mut self,
scope: &AnyScope,
parent: &Element, parent: &Element,
previous_sibling: Option<&Node>, previous_sibling: Option<&Node>,
ancestor: Option<VNode>, ancestor: Option<VNode>,
) -> Option<Node> { ) -> Option<Node> {
match *self { match *self {
VNode::VTag(ref mut vtag) => vtag.apply(parent, previous_sibling, ancestor), VNode::VTag(ref mut vtag) => vtag.apply(scope, parent, previous_sibling, ancestor),
VNode::VText(ref mut vtext) => vtext.apply(parent, previous_sibling, ancestor), VNode::VText(ref mut vtext) => vtext.apply(scope, parent, previous_sibling, ancestor),
VNode::VComp(ref mut vcomp) => vcomp.apply(parent, previous_sibling, ancestor), VNode::VComp(ref mut vcomp) => vcomp.apply(scope, parent, previous_sibling, ancestor),
VNode::VList(ref mut vlist) => vlist.apply(parent, previous_sibling, ancestor), VNode::VList(ref mut vlist) => vlist.apply(scope, parent, previous_sibling, ancestor),
VNode::VRef(ref mut node) => { VNode::VRef(ref mut node) => {
let sibling = match ancestor { let sibling = match ancestor {
Some(mut n) => n.detach(parent), Some(mut n) => n.detach(parent),

View File

@ -3,7 +3,7 @@
use super::{ use super::{
Attributes, Classes, Listener, Listeners, Patch, Reform, Transformer, VDiff, VList, VNode, Attributes, Classes, Listener, Listeners, Patch, Reform, Transformer, VDiff, VList, VNode,
}; };
use crate::html::NodeRef; use crate::html::{AnyScope, NodeRef};
use crate::utils::document; use crate::utils::document;
use cfg_if::cfg_if; use cfg_if::cfg_if;
use cfg_match::cfg_match; use cfg_match::cfg_match;
@ -399,6 +399,7 @@ impl VDiff for VTag {
/// to compute what to patch in the actual DOM nodes. /// to compute what to patch in the actual DOM nodes.
fn apply( fn apply(
&mut self, &mut self,
scope: &AnyScope,
parent: &Element, parent: &Element,
previous_sibling: Option<&Node>, previous_sibling: Option<&Node>,
ancestor: Option<VNode>, ancestor: Option<VNode>,
@ -498,7 +499,7 @@ impl VDiff for VTag {
// Process children // Process children
self.children self.children
.apply(&element, None, ancestor.map(|a| a.children.into())); .apply(scope, &element, None, ancestor.map(|a| a.children.into()));
let node = self.reference.as_ref().map(|e| { let node = self.reference.as_ref().map(|e| {
let node = cfg_match! { let node = cfg_match! {
@ -876,6 +877,7 @@ mod tests {
#[cfg(feature = "web_sys")] #[cfg(feature = "web_sys")]
let document = web_sys::window().unwrap().document().unwrap(); let document = web_sys::window().unwrap().document().unwrap();
let scope = AnyScope::default();
let div_el = document.create_element("div").unwrap(); let div_el = document.create_element("div").unwrap();
let namespace = SVG_NAMESPACE; let namespace = SVG_NAMESPACE;
#[cfg(feature = "web_sys")] #[cfg(feature = "web_sys")]
@ -887,17 +889,17 @@ mod tests {
let mut svg_node = html! { <svg>{path_node}</svg> }; let mut svg_node = html! { <svg>{path_node}</svg> };
let svg_tag = assert_vtag(&mut svg_node); let svg_tag = assert_vtag(&mut svg_node);
svg_tag.apply(&div_el, None, None); svg_tag.apply(&scope, &div_el, None, None);
assert_namespace(svg_tag, SVG_NAMESPACE); assert_namespace(svg_tag, SVG_NAMESPACE);
let path_tag = assert_vtag(svg_tag.children.get_mut(0).unwrap()); let path_tag = assert_vtag(svg_tag.children.get_mut(0).unwrap());
assert_namespace(path_tag, SVG_NAMESPACE); assert_namespace(path_tag, SVG_NAMESPACE);
let g_tag = assert_vtag(&mut g_node); let g_tag = assert_vtag(&mut g_node);
g_tag.apply(&div_el, None, None); g_tag.apply(&scope, &div_el, None, None);
assert_namespace(g_tag, HTML_NAMESPACE); assert_namespace(g_tag, HTML_NAMESPACE);
g_tag.reference = None; g_tag.reference = None;
g_tag.apply(&svg_el, None, None); g_tag.apply(&scope, &svg_el, None, None);
assert_namespace(g_tag, SVG_NAMESPACE); assert_namespace(g_tag, SVG_NAMESPACE);
} }
@ -1019,6 +1021,7 @@ mod tests {
#[test] #[test]
fn swap_order_of_classes() { fn swap_order_of_classes() {
let scope = AnyScope::default();
let parent = document().create_element("div").unwrap(); let parent = document().create_element("div").unwrap();
#[cfg(feature = "std_web")] #[cfg(feature = "std_web")]
@ -1027,7 +1030,7 @@ mod tests {
document().body().unwrap().append_child(&parent).unwrap(); document().body().unwrap().append_child(&parent).unwrap();
let mut elem = html! { <div class=("class-1", "class-2", "class-3")></div> }; let mut elem = html! { <div class=("class-1", "class-2", "class-3")></div> };
elem.apply(&parent, None, None); elem.apply(&scope, &parent, None, None);
let vtag = if let VNode::VTag(vtag) = elem { let vtag = if let VNode::VTag(vtag) = elem {
vtag vtag
@ -1053,7 +1056,7 @@ mod tests {
} else { } else {
panic!("should be vtag") panic!("should be vtag")
}; };
vtag.apply(&parent, None, Some(VNode::VTag(ancestor))); vtag.apply(&scope, &parent, None, Some(VNode::VTag(ancestor)));
let expected = "class-3 class-2 class-1"; let expected = "class-3 class-2 class-1";
assert_eq!(vtag.classes.to_string(), expected); assert_eq!(vtag.classes.to_string(), expected);
@ -1069,6 +1072,7 @@ mod tests {
#[test] #[test]
fn add_class_to_the_middle() { fn add_class_to_the_middle() {
let scope = AnyScope::default();
let parent = document().create_element("div").unwrap(); let parent = document().create_element("div").unwrap();
#[cfg(feature = "std_web")] #[cfg(feature = "std_web")]
@ -1077,7 +1081,7 @@ mod tests {
document().body().unwrap().append_child(&parent).unwrap(); document().body().unwrap().append_child(&parent).unwrap();
let mut elem = html! { <div class=("class-1", "class-3")></div> }; let mut elem = html! { <div class=("class-1", "class-3")></div> };
elem.apply(&parent, None, None); elem.apply(&scope, &parent, None, None);
let vtag = if let VNode::VTag(vtag) = elem { let vtag = if let VNode::VTag(vtag) = elem {
vtag vtag
@ -1103,7 +1107,7 @@ mod tests {
} else { } else {
panic!("should be vtag") panic!("should be vtag")
}; };
vtag.apply(&parent, None, Some(VNode::VTag(ancestor))); vtag.apply(&scope, &parent, None, Some(VNode::VTag(ancestor)));
let expected = "class-1 class-2 class-3"; let expected = "class-1 class-2 class-3";
assert_eq!(vtag.classes.to_string(), expected); assert_eq!(vtag.classes.to_string(), expected);

View File

@ -1,6 +1,7 @@
//! This module contains the implementation of a virtual text node `VText`. //! This module contains the implementation of a virtual text node `VText`.
use super::{Reform, VDiff, VNode}; use super::{Reform, VDiff, VNode};
use crate::html::AnyScope;
use crate::utils::document; use crate::utils::document;
use cfg_if::cfg_if; use cfg_if::cfg_if;
use cfg_match::cfg_match; use cfg_match::cfg_match;
@ -53,6 +54,7 @@ impl VDiff for VText {
/// Renders virtual node over existing `TextNode`, but only if value of text had changed. /// Renders virtual node over existing `TextNode`, but only if value of text had changed.
fn apply( fn apply(
&mut self, &mut self,
_scope: &AnyScope,
parent: &Element, parent: &Element,
previous_sibling: Option<&Node>, previous_sibling: Option<&Node>,
ancestor: Option<VNode>, ancestor: Option<VNode>,