Merge branch 'master' into polish-throw
This commit is contained in:
200 files changed
+5564
-2423
No files matched your search
@@ -4,19 +4,19 @@
|
||||
|
||||
use crate::{
|
||||
any_props::VProps,
|
||||
arena::{ElementId, ElementRef},
|
||||
innerlude::{DirtyScope, ErrorBoundary, Mutations, Scheduler, SchedulerMsg},
|
||||
arena::ElementId,
|
||||
innerlude::{DirtyScope, ElementRef, ErrorBoundary, Mutations, Scheduler, SchedulerMsg},
|
||||
mutations::Mutation,
|
||||
nodes::RenderReturn,
|
||||
nodes::{Template, TemplateId},
|
||||
runtime::{Runtime, RuntimeGuard},
|
||||
scopes::{ScopeId, ScopeState},
|
||||
AttributeValue, Element, Event, Scope,
|
||||
AttributeValue, Element, Event, Scope, VNode,
|
||||
};
|
||||
use futures_util::{pin_mut, StreamExt};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use slab::Slab;
|
||||
use std::{any::Any, cell::Cell, collections::BTreeSet, future::Future, rc::Rc, sync::Arc};
|
||||
use std::{any::Any, cell::Cell, collections::BTreeSet, future::Future, ptr::NonNull, rc::Rc, sync::Arc};
|
||||
|
||||
/// A virtual node system that progresses user events and diffs UI trees.
|
||||
///
|
||||
@@ -186,7 +186,10 @@ pub struct VirtualDom {
|
||||
pub(crate) templates: FxHashMap<TemplateId, FxHashMap<usize, Template<'static>>>,
|
||||
|
||||
// Every element is actually a dual reference - one to the template and the other to the dynamic node in that template
|
||||
pub(crate) elements: Slab<ElementRef>,
|
||||
pub(crate) element_refs: Slab<Option<NonNull<VNode<'static>>>>,
|
||||
|
||||
// The element ids that are used in the renderer
|
||||
pub(crate) elements: Slab<Option<ElementRef>>,
|
||||
|
||||
pub(crate) mutations: Mutations<'static>,
|
||||
|
||||
@@ -263,6 +266,7 @@ impl VirtualDom {
|
||||
dirty_scopes: Default::default(),
|
||||
templates: Default::default(),
|
||||
elements: Default::default(),
|
||||
element_refs: Default::default(),
|
||||
mutations: Mutations::default(),
|
||||
suspended_scopes: Default::default(),
|
||||
};
|
||||
@@ -279,7 +283,7 @@ impl VirtualDom {
|
||||
)));
|
||||
|
||||
// the root element is always given element ID 0 since it's the container for the entire tree
|
||||
dom.elements.insert(ElementRef::none());
|
||||
dom.elements.insert(None);
|
||||
|
||||
dom
|
||||
}
|
||||
@@ -317,9 +321,9 @@ impl VirtualDom {
|
||||
}
|
||||
}
|
||||
|
||||
/// Call a listener inside the VirtualDom with data from outside the VirtualDom.
|
||||
/// Call a listener inside the VirtualDom with data from outside the VirtualDom. **The ElementId passed in must be the id of an dynamic element, not a static node or a text node.**
|
||||
///
|
||||
/// This method will identify the appropriate element. The data must match up with the listener delcared. Note that
|
||||
/// This method will identify the appropriate element. The data must match up with the listener declared. Note that
|
||||
/// this method does not give any indication as to the success of the listener call. If the listener is not found,
|
||||
/// nothing will happen.
|
||||
///
|
||||
@@ -356,7 +360,15 @@ impl VirtualDom {
|
||||
| | | <-- no, broke early
|
||||
| <-- no, broke early
|
||||
*/
|
||||
let mut parent_path = self.elements.get(element.0);
|
||||
let parent_path = match self.elements.get(element.0) {
|
||||
Some(Some(el)) => el,
|
||||
_ => return,
|
||||
};
|
||||
let mut parent_node = self
|
||||
.element_refs
|
||||
.get(parent_path.template.0)
|
||||
.cloned()
|
||||
.map(|el| (*parent_path, el));
|
||||
let mut listeners = vec![];
|
||||
|
||||
// We will clone this later. The data itself is wrapped in RC to be used in callbacks if required
|
||||
@@ -368,36 +380,72 @@ impl VirtualDom {
|
||||
// If the event bubbles, we traverse through the tree until we find the target element.
|
||||
if bubbles {
|
||||
// Loop through each dynamic attribute (in a depth first order) in this template before moving up to the template's parent.
|
||||
while let Some(el_ref) = parent_path {
|
||||
while let Some((path, el_ref)) = parent_node {
|
||||
// safety: we maintain references of all vnodes in the element slab
|
||||
if let Some(template) = el_ref.template {
|
||||
let template = unsafe { template.as_ref() };
|
||||
let node_template = template.template.get();
|
||||
let target_path = el_ref.path;
|
||||
let template = unsafe { el_ref.unwrap().as_ref() };
|
||||
let node_template = template.template.get();
|
||||
let target_path = path.path;
|
||||
|
||||
for (idx, attr) in template.dynamic_attrs.iter().enumerate() {
|
||||
let this_path = node_template.attr_paths[idx];
|
||||
for (idx, attr) in template.dynamic_attrs.iter().enumerate() {
|
||||
let this_path = node_template.attr_paths[idx];
|
||||
|
||||
// Remove the "on" prefix if it exists, TODO, we should remove this and settle on one
|
||||
if attr.name.trim_start_matches("on") == name
|
||||
&& target_path.is_decendant(&this_path)
|
||||
{
|
||||
listeners.push(&attr.value);
|
||||
// Remove the "on" prefix if it exists, TODO, we should remove this and settle on one
|
||||
if attr.name.trim_start_matches("on") == name
|
||||
&& target_path.is_decendant(&this_path)
|
||||
{
|
||||
listeners.push(&attr.value);
|
||||
|
||||
// Break if this is the exact target element.
|
||||
// This means we won't call two listeners with the same name on the same element. This should be
|
||||
// documented, or be rejected from the rsx! macro outright
|
||||
if target_path == this_path {
|
||||
break;
|
||||
}
|
||||
// Break if this is the exact target element.
|
||||
// This means we won't call two listeners with the same name on the same element. This should be
|
||||
// documented, or be rejected from the rsx! macro outright
|
||||
if target_path == this_path {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we've accumulated all the parent attributes for the target element, call them in reverse order
|
||||
// We check the bubble state between each call to see if the event has been stopped from bubbling
|
||||
for listener in listeners.drain(..).rev() {
|
||||
if let AttributeValue::Listener(listener) = listener {
|
||||
let origin = el_ref.scope;
|
||||
// Now that we've accumulated all the parent attributes for the target element, call them in reverse order
|
||||
// We check the bubble state between each call to see if the event has been stopped from bubbling
|
||||
for listener in listeners.drain(..).rev() {
|
||||
if let AttributeValue::Listener(listener) = listener {
|
||||
let origin = path.scope;
|
||||
self.runtime.scope_stack.borrow_mut().push(origin);
|
||||
self.runtime.rendering.set(false);
|
||||
if let Some(cb) = listener.borrow_mut().as_deref_mut() {
|
||||
cb(uievent.clone());
|
||||
}
|
||||
self.runtime.scope_stack.borrow_mut().pop();
|
||||
self.runtime.rendering.set(true);
|
||||
|
||||
if !uievent.propagates.get() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parent_node = template.parent.get().and_then(|element_ref| {
|
||||
self.element_refs
|
||||
.get(element_ref.template.0)
|
||||
.cloned()
|
||||
.map(|el| (element_ref, el))
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Otherwise, we just call the listener on the target element
|
||||
if let Some((path, el_ref)) = parent_node {
|
||||
// safety: we maintain references of all vnodes in the element slab
|
||||
let template = unsafe { el_ref.unwrap().as_ref() };
|
||||
let node_template = template.template.get();
|
||||
let target_path = path.path;
|
||||
|
||||
for (idx, attr) in template.dynamic_attrs.iter().enumerate() {
|
||||
let this_path = node_template.attr_paths[idx];
|
||||
|
||||
// Remove the "on" prefix if it exists, TODO, we should remove this and settle on one
|
||||
// Only call the listener if this is the exact target element.
|
||||
if attr.name.trim_start_matches("on") == name && target_path == this_path {
|
||||
if let AttributeValue::Listener(listener) = &attr.value {
|
||||
let origin = path.scope;
|
||||
self.runtime.scope_stack.borrow_mut().push(origin);
|
||||
self.runtime.rendering.set(false);
|
||||
if let Some(cb) = listener.borrow_mut().as_deref_mut() {
|
||||
@@ -406,44 +454,7 @@ impl VirtualDom {
|
||||
self.runtime.scope_stack.borrow_mut().pop();
|
||||
self.runtime.rendering.set(true);
|
||||
|
||||
if !uievent.propagates.get() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parent_path = template.parent.and_then(|id| self.elements.get(id.0));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Otherwise, we just call the listener on the target element
|
||||
if let Some(el_ref) = parent_path {
|
||||
// safety: we maintain references of all vnodes in the element slab
|
||||
if let Some(template) = el_ref.template {
|
||||
let template = unsafe { template.as_ref() };
|
||||
let node_template = template.template.get();
|
||||
let target_path = el_ref.path;
|
||||
|
||||
for (idx, attr) in template.dynamic_attrs.iter().enumerate() {
|
||||
let this_path = node_template.attr_paths[idx];
|
||||
|
||||
// Remove the "on" prefix if it exists, TODO, we should remove this and settle on one
|
||||
// Only call the listener if this is the exact target element.
|
||||
if attr.name.trim_start_matches("on") == name && target_path == this_path {
|
||||
if let AttributeValue::Listener(listener) = &attr.value {
|
||||
let origin = el_ref.scope;
|
||||
self.runtime.scope_stack.borrow_mut().push(origin);
|
||||
self.runtime.rendering.set(false);
|
||||
if let Some(cb) = listener.borrow_mut().as_deref_mut() {
|
||||
cb(uievent.clone());
|
||||
}
|
||||
self.runtime.scope_stack.borrow_mut().pop();
|
||||
self.runtime.rendering.set(true);
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -566,7 +577,7 @@ impl VirtualDom {
|
||||
// If an error occurs, we should try to render the default error component and context where the error occured
|
||||
RenderReturn::Aborted(placeholder) => {
|
||||
tracing::debug!("Ran into suspended or aborted scope during rebuild");
|
||||
let id = self.next_null();
|
||||
let id = self.next_element();
|
||||
placeholder.id.set(Some(id));
|
||||
self.mutations.push(Mutation::CreatePlaceholder { id });
|
||||
}
|
||||
@@ -598,15 +609,12 @@ impl VirtualDom {
|
||||
/// The mutations will be thrown out, so it's best to use this method for things like SSR that have async content
|
||||
pub async fn wait_for_suspense(&mut self) {
|
||||
loop {
|
||||
// println!("waiting for suspense {:?}", self.suspended_scopes);
|
||||
if self.suspended_scopes.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// println!("waiting for suspense");
|
||||
self.wait_for_work().await;
|
||||
|
||||
// println!("Rendered immediately");
|
||||
_ = self.render_immediate();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user