mirror of https://github.com/yewstack/yew
Add component and parent access to scope (#1151)
* Add reference to parent to scope * Add untyped scope
This commit is contained in:
parent
404ff77ffb
commit
9c0951513d
|
@ -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 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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>,
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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>,
|
||||||
|
|
Loading…
Reference in New Issue