Merge branch 'master' into jk/breaking-merged

This commit is contained in:
Jonathan Kelley committed 2024-01-11 16:04:36 -08:00
commit 2ae3241cc0
211 files changed
+21135 -3770

No files matched your search

+1 -1
View File
@@ -7,7 +7,7 @@ use crate::{
use std::{
any::{Any, TypeId},
backtrace::Backtrace,
cell::RefCell,
cell::{Cell, RefCell},
error::Error,
fmt::{Debug, Display},
rc::Rc,
+57
View File
@@ -568,6 +568,51 @@ pub enum TemplateAttribute {
},
}
/// An attribute with information about its position in the DOM and the element it was mounted to
#[derive(Debug)]
pub struct MountedAttribute<'a> {
pub(crate) ty: AttributeType<'a>,
/// The element in the DOM that this attribute belongs to
pub(crate) mounted_element: Cell<ElementId>,
}
impl<'a> From<Attribute<'a>> for MountedAttribute<'a> {
fn from(attr: Attribute<'a>) -> Self {
Self {
ty: AttributeType::Single(attr),
mounted_element: Default::default(),
}
}
}
impl<'a> From<&'a [Attribute<'a>]> for MountedAttribute<'a> {
fn from(attr: &'a [Attribute<'a>]) -> Self {
Self {
ty: AttributeType::Many(attr),
mounted_element: Default::default(),
}
}
}
impl<'a> From<&'a Vec<Attribute<'a>>> for MountedAttribute<'a> {
fn from(attr: &'a Vec<Attribute<'a>>) -> Self {
attr.as_slice().into()
}
}
impl<'a> MountedAttribute<'a> {
/// Get the type of this attribute
pub fn attribute_type(&self) -> &AttributeType<'a> {
&self.ty
}
/// Get the element that this attribute is mounted to
pub fn mounted_element(&self) -> ElementId {
self.mounted_element.get()
}
}
/// An attribute on a DOM node, such as `id="my-thing"` or `href="https://example.com"`
#[derive(Debug)]
pub struct Attribute {
@@ -875,3 +920,15 @@ impl<T: IntoAttributeValue> IntoAttributeValue for Option<T> {
}
}
}
/// A trait for anything that has a dynamic list of attributes
pub trait HasAttributes<'a> {
/// Push an attribute onto the list of attributes
fn push_attribute(
self,
name: &'a str,
ns: Option<&'static str>,
attr: impl IntoAttributeValue<'a>,
volatile: bool,
) -> Self;
}
+21
View File
@@ -95,6 +95,16 @@ impl Runtime {
self.scope_stack.borrow().last().copied()
}
/// Call this function with the current scope set to the given scope
///
/// Useful in a limited number of scenarios, not public.
pub(crate) fn with_scope<O>(&self, id: ScopeId, f: impl FnOnce() -> O) -> O {
self.scope_stack.borrow_mut().push(id);
let o = f();
self.scope_stack.borrow_mut().pop();
o
}
/// Get the context for any scope given its ID
///
/// This is useful for inserting or removing contexts from a scope, or rendering out its root node
@@ -145,6 +155,17 @@ impl RuntimeGuard {
push_runtime(runtime.clone());
Self(runtime)
}
/// Run a function with a given runtime and scope in context
pub fn with<O>(runtime: Rc<Runtime>, scope: Option<ScopeId>, f: impl FnOnce() -> O) -> O {
let guard = Self::new(runtime.clone());
let o = match scope {
Some(scope) => Runtime::with_scope(&runtime, scope, f),
None => f(),
};
drop(guard);
o
}
}
impl Drop for RuntimeGuard {
+16 -4
View File
@@ -114,7 +114,7 @@ impl ScopeContext {
}
let mut search_parent = self.parent_id;
let context = with_runtime(|runtime: &crate::runtime::Runtime| {
let cur_runtime = with_runtime(|runtime: &crate::runtime::Runtime| {
while let Some(parent_id) = search_parent {
let parent = runtime.get_context(parent_id).unwrap();
tracing::trace!(
@@ -132,9 +132,9 @@ impl ScopeContext {
search_parent = parent.parent_id;
}
None
})
.flatten();
match context {
});
match cur_runtime.flatten() {
Some(ctx) => Some(ctx),
None => {
tracing::trace!(
@@ -389,3 +389,15 @@ impl ScopeId {
with_scope(self, |cx| cx.height()).expect("to be in a dioxus runtime")
}
}
/// Spawn a future on a component given its [`ScopeId`].
pub fn spawn_at(fut: impl Future<Output = ()> + 'static, scope_id: ScopeId) -> Option<TaskId> {
with_runtime(|rt| rt.get_context(scope_id).unwrap().push_future(fut))
}
/// Spawn a future that Dioxus won't clean up when this component is unmounted
///
/// This is good for tasks that need to be run after the component has been dropped.
pub fn spawn_forever(fut: impl Future<Output = ()> + 'static) -> Option<TaskId> {
spawn_at(fut, ScopeId(0))
}
+1
View File
@@ -41,6 +41,7 @@ pub struct ScopeState {
impl Drop for ScopeState {
fn drop(&mut self) {
self.drop_listeners();
self.runtime.remove_context(self.context_id);
}
}
+63 -37
View File
@@ -78,7 +78,7 @@ use std::{any::Any, cell::Cell, collections::BTreeSet, future::Future, rc::Rc};
/// #[component]
/// fn Title<'a>( children: Element) -> Element {
/// cx.render(rsx! {
/// div { id: "title", children }
/// div { id: "title", {children} }
/// })
/// }
/// ```
@@ -394,17 +394,14 @@ impl VirtualDom {
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);
// 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;
}
if target_path.is_decendant(&this_path) {
attr.ty.for_each(|attribute| {
if attribute.name.trim_start_matches("on") == name {
if let AttributeValue::Listener(listener) = &attribute.value {
listeners.push(listener);
}
}
});
}
}
@@ -416,38 +413,67 @@ impl VirtualDom {
listener.call(uievent.clone());
self.runtime.rendering.set(true);
// let origin: ScopeId = 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;
}
}
}
let mount = el_ref.mount.get().as_usize();
parent_node = mount.and_then(|id| self.mounts.get(id).and_then(|el| el.parent));
}
} else {
// Otherwise, we just call the listener on the target element
if let Some(path) = parent_node {
let el_ref = &self.mounts[path.mount.0].node;
let node_template = el_ref.template.get();
let target_path = path.path;
for (idx, attr) in el_ref.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 {
self.runtime.rendering.set(false);
listener.call(uievent.clone());
self.runtime.rendering.set(true);
break;
}
}
let mount = el_ref.mount.get().as_usize();
parent_node = mount.and_then(|id| self.mounts.get(id).and_then(|el| el.parent));
}
}
// else {
// // Otherwise, we just call the listener on the target element
// if let Some(path) = parent_node {
// let el_ref = &self.mounts[path.mount.0].node;
// let node_template = el_ref.template.get();
// let target_path = path.path;
// for (idx, attr) in el_ref.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 {
// self.runtime.rendering.set(false);
// listener.call(uievent.clone());
// self.runtime.rendering.set(true);
// // if target_path == this_path {
// // let mut should_stop = false;
// // attr.ty.for_each(|attribute| {
// // if attribute.name.trim_start_matches("on") == name {
// // if let AttributeValue::Listener(listener) = &attribute.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() {
// // cb(uievent.clone());
// // }
// // self.runtime.scope_stack.borrow_mut().pop();
// // self.runtime.rendering.set(true);
// should_stop = true;
// }
// }
// // });
// if should_stop {
// return;
// }
// }
// }
// }
}
}