mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-10 06:34:20 +00:00
rip out more lifetimes
This commit is contained in:
parent
c9ff449e45
commit
3ad16ddd37
18 changed files with 165 additions and 456 deletions
|
@ -10,9 +10,6 @@ homepage = "https://dioxuslabs.com"
|
|||
keywords = ["dom", "ui", "gui", "react"]
|
||||
|
||||
[dependencies]
|
||||
# Bumpalo is used as a micro heap backing each component
|
||||
bumpalo = { version = "3.6", features = ["collections", "boxed"] }
|
||||
|
||||
# faster hashmaps
|
||||
rustc-hash = { workspace = true }
|
||||
|
||||
|
|
|
@ -11,9 +11,9 @@ use std::panic::AssertUnwindSafe;
|
|||
/// # Safety
|
||||
///
|
||||
/// This should not be implemented outside this module
|
||||
pub(crate) unsafe trait AnyProps<'a> {
|
||||
pub(crate) unsafe trait AnyProps {
|
||||
fn props_ptr(&self) -> *const ();
|
||||
fn render(&'a self, bump: &'a ScopeState) -> RenderReturn<'a>;
|
||||
fn render<'a>(&'a self, bump: &'a ScopeState) -> RenderReturn<'a>;
|
||||
unsafe fn memoize(&self, other: &dyn AnyProps) -> bool;
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ impl<'a, P> VProps<'a, P> {
|
|||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a, P> AnyProps<'a> for VProps<'a, P> {
|
||||
unsafe impl<'a, P> AnyProps for VProps<'a, P> {
|
||||
fn props_ptr(&self) -> *const () {
|
||||
&self.props as *const _ as *const ()
|
||||
}
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
use crate::nodes::RenderReturn;
|
||||
use crate::{Attribute, AttributeValue, VComponent};
|
||||
use bumpalo::Bump;
|
||||
use std::cell::RefCell;
|
||||
use std::cell::{Cell, UnsafeCell};
|
||||
|
||||
pub(crate) struct BumpFrame {
|
||||
pub bump: UnsafeCell<Bump>,
|
||||
pub node: Cell<*const RenderReturn<'static>>,
|
||||
|
||||
// The bump allocator will not call the destructor of the objects it allocated. Attributes and props need to have there destructor called, so we keep a list of them to drop before the bump allocator is reset.
|
||||
pub(crate) attributes_to_drop_before_reset: RefCell<Vec<*const Attribute<'static>>>,
|
||||
pub(crate) props_to_drop_before_reset: RefCell<Vec<*const VComponent<'static>>>,
|
||||
}
|
||||
|
||||
impl BumpFrame {
|
||||
pub(crate) fn new(capacity: usize) -> Self {
|
||||
let bump = Bump::with_capacity(capacity);
|
||||
Self {
|
||||
bump: UnsafeCell::new(bump),
|
||||
node: Cell::new(std::ptr::null()),
|
||||
attributes_to_drop_before_reset: Default::default(),
|
||||
props_to_drop_before_reset: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new lifetime out of thin air
|
||||
pub(crate) unsafe fn try_load_node<'b>(&self) -> Option<&'b RenderReturn<'b>> {
|
||||
let node = self.node.get();
|
||||
|
||||
if node.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
unsafe { std::mem::transmute(&*node) }
|
||||
}
|
||||
|
||||
pub(crate) fn bump(&self) -> &Bump {
|
||||
unsafe { &*self.bump.get() }
|
||||
}
|
||||
|
||||
pub(crate) fn add_attribute_to_drop(&self, attribute: *const Attribute<'static>) {
|
||||
self.attributes_to_drop_before_reset
|
||||
.borrow_mut()
|
||||
.push(attribute);
|
||||
}
|
||||
|
||||
/// Reset the bump allocator and drop all the attributes and props that were allocated in it.
|
||||
///
|
||||
/// # Safety
|
||||
/// The caller must insure that no reference to anything allocated in the bump allocator is available after this function is called.
|
||||
pub(crate) unsafe fn reset(&self) {
|
||||
let mut attributes = self.attributes_to_drop_before_reset.borrow_mut();
|
||||
attributes.drain(..).for_each(|attribute| {
|
||||
let attribute = unsafe { &*attribute };
|
||||
if let AttributeValue::Any(l) = &attribute.value {
|
||||
_ = l.take();
|
||||
}
|
||||
});
|
||||
let mut props = self.props_to_drop_before_reset.borrow_mut();
|
||||
props.drain(..).for_each(|prop| {
|
||||
let prop = unsafe { &*prop };
|
||||
_ = prop.props.borrow_mut().take();
|
||||
});
|
||||
unsafe {
|
||||
let bump = &mut *self.bump.get();
|
||||
bump.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for BumpFrame {
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.reset() }
|
||||
}
|
||||
}
|
|
@ -67,7 +67,7 @@ impl<'b> VirtualDom {
|
|||
/// Create a new template [`VNode`] and write it to the [`Mutations`] buffer.
|
||||
///
|
||||
/// This method pushes the ScopeID to the internal scopestack and returns the number of nodes created.
|
||||
pub(crate) fn create_scope(&mut self, scope: ScopeId, template: &'b VNode<'b>) -> usize {
|
||||
pub(crate) fn create_scope(&mut self, scope: ScopeId, template: &'b VNode) -> usize {
|
||||
self.runtime.scope_stack.borrow_mut().push(scope);
|
||||
let nodes = self.create(template);
|
||||
self.runtime.scope_stack.borrow_mut().pop();
|
||||
|
@ -75,7 +75,7 @@ impl<'b> VirtualDom {
|
|||
}
|
||||
|
||||
/// Create this template and write its mutations
|
||||
pub(crate) fn create(&mut self, node: &'b VNode<'b>) -> usize {
|
||||
pub(crate) fn create(&mut self, node: &'b VNode) -> usize {
|
||||
// check for a overriden template
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
|
@ -182,7 +182,7 @@ impl<'b> VirtualDom {
|
|||
1
|
||||
}
|
||||
|
||||
fn write_dynamic_root(&mut self, template: &'b VNode<'b>, idx: usize) -> usize {
|
||||
fn write_dynamic_root(&mut self, template: &'b VNode, idx: usize) -> usize {
|
||||
use DynamicNode::*;
|
||||
match &template.dynamic_nodes[idx] {
|
||||
node @ Component { .. } | node @ Fragment(_) => {
|
||||
|
@ -232,7 +232,7 @@ impl<'b> VirtualDom {
|
|||
/// We want to make sure we write these nodes while on top of the root
|
||||
fn write_element_root(
|
||||
&mut self,
|
||||
template: &'b VNode<'b>,
|
||||
template: &'b VNode,
|
||||
root_idx: usize,
|
||||
dynamic_attrs: &mut Peekable<impl Iterator<Item = (usize, &'static [u8])>>,
|
||||
dynamic_nodes_iter: &mut Peekable<impl Iterator<Item = ((usize, usize), &'static [u8])>>,
|
||||
|
@ -269,7 +269,7 @@ impl<'b> VirtualDom {
|
|||
dynamic_nodes_iter: &mut Peekable<impl Iterator<Item = ((usize, usize), &'static [u8])>>,
|
||||
dynamic_nodes: &[(usize, &'static [u8])],
|
||||
root_idx: u8,
|
||||
template: &'b VNode<'b>,
|
||||
template: &'b VNode,
|
||||
) {
|
||||
let (start, end) = match collect_dyn_node_range(dynamic_nodes_iter, root_idx) {
|
||||
Some((a, b)) => (a, b),
|
||||
|
@ -306,7 +306,7 @@ impl<'b> VirtualDom {
|
|||
attrs: &mut Peekable<impl Iterator<Item = (usize, &'static [u8])>>,
|
||||
root_idx: u8,
|
||||
root: ElementId,
|
||||
node: &'b VNode<'b>,
|
||||
node: &'b VNode,
|
||||
) {
|
||||
while let Some((mut attr_id, path)) =
|
||||
attrs.next_if(|(_, p)| p.first().copied() == Some(root_idx))
|
||||
|
@ -327,7 +327,7 @@ impl<'b> VirtualDom {
|
|||
|
||||
fn write_attribute(
|
||||
&mut self,
|
||||
template: &'b VNode<'b>,
|
||||
template: &'b VNode,
|
||||
idx: usize,
|
||||
attribute: &'b crate::Attribute<'b>,
|
||||
id: ElementId,
|
||||
|
@ -490,7 +490,7 @@ impl<'b> VirtualDom {
|
|||
}
|
||||
}
|
||||
|
||||
fn create_dynamic_text(&mut self, parent: ElementRef, text: &'b VText<'b>) -> usize {
|
||||
fn create_dynamic_text(&mut self, parent: ElementRef, text: &'b VText) -> usize {
|
||||
// Allocate a dynamic element reference for this text node
|
||||
let new_id = self.next_element();
|
||||
|
||||
|
@ -538,7 +538,7 @@ impl<'b> VirtualDom {
|
|||
pub(super) fn create_component_node(
|
||||
&mut self,
|
||||
parent: Option<ElementRef>,
|
||||
component: &'b VComponent<'b>,
|
||||
component: &'b VComponent,
|
||||
) -> usize {
|
||||
use RenderReturn::*;
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ impl<'b> VirtualDom {
|
|||
self.runtime.scope_stack.borrow_mut().pop();
|
||||
}
|
||||
|
||||
fn diff_ok_to_err(&mut self, l: &'b VNode<'b>, p: &'b VPlaceholder) {
|
||||
fn diff_ok_to_err(&mut self, l: &'b VNode, p: &'b VPlaceholder) {
|
||||
let id = self.next_element();
|
||||
p.id.set(Some(id));
|
||||
p.parent.set(l.parent.get());
|
||||
|
@ -82,7 +82,7 @@ impl<'b> VirtualDom {
|
|||
};
|
||||
}
|
||||
|
||||
fn diff_node(&mut self, left_template: &'b VNode<'b>, right_template: &'b VNode<'b>) {
|
||||
fn diff_node(&mut self, left_template: &'b VNode, right_template: &'b VNode) {
|
||||
// If hot reloading is enabled, we need to make sure we're using the latest template
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
|
@ -198,8 +198,8 @@ impl<'b> VirtualDom {
|
|||
|
||||
fn diff_vcomponent(
|
||||
&mut self,
|
||||
left: &'b VComponent<'b>,
|
||||
right: &'b VComponent<'b>,
|
||||
left: &'b VComponent,
|
||||
right: &'b VComponent,
|
||||
parent: Option<ElementRef>,
|
||||
) {
|
||||
if std::ptr::eq(left, right) {
|
||||
|
@ -249,8 +249,8 @@ impl<'b> VirtualDom {
|
|||
|
||||
fn replace_vcomponent(
|
||||
&mut self,
|
||||
right: &'b VComponent<'b>,
|
||||
left: &'b VComponent<'b>,
|
||||
right: &'b VComponent,
|
||||
left: &'b VComponent,
|
||||
parent: Option<ElementRef>,
|
||||
) {
|
||||
let m = self.create_component_node(parent, right);
|
||||
|
@ -307,7 +307,7 @@ impl<'b> VirtualDom {
|
|||
/// Component { ..props }
|
||||
/// }
|
||||
/// ```
|
||||
fn light_diff_templates(&mut self, left: &'b VNode<'b>, right: &'b VNode<'b>) {
|
||||
fn light_diff_templates(&mut self, left: &'b VNode, right: &'b VNode) {
|
||||
let parent = left.parent.take();
|
||||
match matching_components(left, right) {
|
||||
None => self.replace(left, [right], parent),
|
||||
|
@ -321,7 +321,7 @@ impl<'b> VirtualDom {
|
|||
///
|
||||
/// This just moves the ID of the old node over to the new node, and then sets the text of the new node if it's
|
||||
/// different.
|
||||
fn diff_vtext(&mut self, left: &'b VText<'b>, right: &'b VText<'b>) {
|
||||
fn diff_vtext(&mut self, left: &'b VText, right: &'b VText) {
|
||||
let id = left.id.get().unwrap_or_else(|| self.next_element());
|
||||
|
||||
right.id.set(Some(id));
|
||||
|
@ -331,12 +331,7 @@ impl<'b> VirtualDom {
|
|||
}
|
||||
}
|
||||
|
||||
fn diff_non_empty_fragment(
|
||||
&mut self,
|
||||
old: &'b [VNode<'b>],
|
||||
new: &'b [VNode<'b>],
|
||||
parent: ElementRef,
|
||||
) {
|
||||
fn diff_non_empty_fragment(&mut self, old: &'b [VNode], new: &'b [VNode], parent: ElementRef) {
|
||||
let new_is_keyed = new[0].key.is_some();
|
||||
let old_is_keyed = old[0].key.is_some();
|
||||
debug_assert!(
|
||||
|
@ -363,12 +358,7 @@ impl<'b> VirtualDom {
|
|||
// [... parent]
|
||||
//
|
||||
// the change list stack is in the same state when this function returns.
|
||||
fn diff_non_keyed_children(
|
||||
&mut self,
|
||||
old: &'b [VNode<'b>],
|
||||
new: &'b [VNode<'b>],
|
||||
parent: ElementRef,
|
||||
) {
|
||||
fn diff_non_keyed_children(&mut self, old: &'b [VNode], new: &'b [VNode], parent: ElementRef) {
|
||||
use std::cmp::Ordering;
|
||||
|
||||
// Handled these cases in `diff_children` before calling this function.
|
||||
|
@ -404,15 +394,10 @@ impl<'b> VirtualDom {
|
|||
// https://github.com/infernojs/inferno/blob/36fd96/packages/inferno/src/DOM/patching.ts#L530-L739
|
||||
//
|
||||
// The stack is empty upon entry.
|
||||
fn diff_keyed_children(
|
||||
&mut self,
|
||||
old: &'b [VNode<'b>],
|
||||
new: &'b [VNode<'b>],
|
||||
parent: ElementRef,
|
||||
) {
|
||||
fn diff_keyed_children(&mut self, old: &'b [VNode], new: &'b [VNode], parent: ElementRef) {
|
||||
if cfg!(debug_assertions) {
|
||||
let mut keys = rustc_hash::FxHashSet::default();
|
||||
let mut assert_unique_keys = |children: &'b [VNode<'b>]| {
|
||||
let mut assert_unique_keys = |children: &'b [VNode]| {
|
||||
keys.clear();
|
||||
for child in children {
|
||||
let key = child.key;
|
||||
|
@ -485,8 +470,8 @@ impl<'b> VirtualDom {
|
|||
/// If there is no offset, then this function returns None and the diffing is complete.
|
||||
fn diff_keyed_ends(
|
||||
&mut self,
|
||||
old: &'b [VNode<'b>],
|
||||
new: &'b [VNode<'b>],
|
||||
old: &'b [VNode],
|
||||
new: &'b [VNode],
|
||||
parent: ElementRef,
|
||||
) -> Option<(usize, usize)> {
|
||||
let mut left_offset = 0;
|
||||
|
@ -542,12 +527,7 @@ impl<'b> VirtualDom {
|
|||
//
|
||||
// Upon exit from this function, it will be restored to that same self.
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn diff_keyed_middle(
|
||||
&mut self,
|
||||
old: &'b [VNode<'b>],
|
||||
new: &'b [VNode<'b>],
|
||||
parent: ElementRef,
|
||||
) {
|
||||
fn diff_keyed_middle(&mut self, old: &'b [VNode], new: &'b [VNode], parent: ElementRef) {
|
||||
/*
|
||||
1. Map the old keys into a numerical ordering based on indices.
|
||||
2. Create a map of old key to its index
|
||||
|
@ -729,7 +709,7 @@ impl<'b> VirtualDom {
|
|||
}
|
||||
|
||||
/// Push all the real nodes on the stack
|
||||
fn push_all_real_nodes(&mut self, node: &'b VNode<'b>) -> usize {
|
||||
fn push_all_real_nodes(&mut self, node: &'b VNode) -> usize {
|
||||
node.template
|
||||
.get()
|
||||
.roots
|
||||
|
@ -783,7 +763,7 @@ impl<'b> VirtualDom {
|
|||
|
||||
pub(crate) fn create_children(
|
||||
&mut self,
|
||||
nodes: impl IntoIterator<Item = &'b VNode<'b>>,
|
||||
nodes: impl IntoIterator<Item = &'b VNode>,
|
||||
parent: Option<ElementRef>,
|
||||
) -> usize {
|
||||
nodes
|
||||
|
@ -797,8 +777,8 @@ impl<'b> VirtualDom {
|
|||
|
||||
fn create_and_insert_before(
|
||||
&mut self,
|
||||
new: &'b [VNode<'b>],
|
||||
before: &'b VNode<'b>,
|
||||
new: &'b [VNode],
|
||||
before: &'b VNode,
|
||||
parent: ElementRef,
|
||||
) {
|
||||
let m = self.create_children(new, Some(parent));
|
||||
|
@ -806,12 +786,7 @@ impl<'b> VirtualDom {
|
|||
self.mutations.push(Mutation::InsertBefore { id, m })
|
||||
}
|
||||
|
||||
fn create_and_insert_after(
|
||||
&mut self,
|
||||
new: &'b [VNode<'b>],
|
||||
after: &'b VNode<'b>,
|
||||
parent: ElementRef,
|
||||
) {
|
||||
fn create_and_insert_after(&mut self, new: &'b [VNode], after: &'b VNode, parent: ElementRef) {
|
||||
let m = self.create_children(new, Some(parent));
|
||||
let id = self.find_last_element(after);
|
||||
self.mutations.push(Mutation::InsertAfter { id, m })
|
||||
|
@ -821,7 +796,7 @@ impl<'b> VirtualDom {
|
|||
fn replace_placeholder(
|
||||
&mut self,
|
||||
l: &'b VPlaceholder,
|
||||
r: impl IntoIterator<Item = &'b VNode<'b>>,
|
||||
r: impl IntoIterator<Item = &'b VNode>,
|
||||
parent: ElementRef,
|
||||
) {
|
||||
let m = self.create_children(r, Some(parent));
|
||||
|
@ -832,8 +807,8 @@ impl<'b> VirtualDom {
|
|||
|
||||
fn replace(
|
||||
&mut self,
|
||||
left: &'b VNode<'b>,
|
||||
right: impl IntoIterator<Item = &'b VNode<'b>>,
|
||||
left: &'b VNode,
|
||||
right: impl IntoIterator<Item = &'b VNode>,
|
||||
parent: Option<ElementRef>,
|
||||
) {
|
||||
let m = self.create_children(right, parent);
|
||||
|
@ -855,7 +830,7 @@ impl<'b> VirtualDom {
|
|||
};
|
||||
}
|
||||
|
||||
fn node_to_placeholder(&mut self, l: &'b [VNode<'b>], r: &'b VPlaceholder, parent: ElementRef) {
|
||||
fn node_to_placeholder(&mut self, l: &'b [VNode], r: &'b VPlaceholder, parent: ElementRef) {
|
||||
// Create the placeholder first, ensuring we get a dedicated ID for the placeholder
|
||||
let placeholder = self.next_element();
|
||||
|
||||
|
@ -878,14 +853,14 @@ impl<'b> VirtualDom {
|
|||
|
||||
/// Remove these nodes from the dom
|
||||
/// Wont generate mutations for the inner nodes
|
||||
fn remove_nodes(&mut self, nodes: &'b [VNode<'b>]) {
|
||||
fn remove_nodes(&mut self, nodes: &'b [VNode]) {
|
||||
nodes
|
||||
.iter()
|
||||
.rev()
|
||||
.for_each(|node| self.remove_node(node, true));
|
||||
}
|
||||
|
||||
fn remove_node(&mut self, node: &'b VNode<'b>, gen_muts: bool) {
|
||||
fn remove_node(&mut self, node: &'b VNode, gen_muts: bool) {
|
||||
// Clean up any attributes that have claimed a static node as dynamic for mount/unmounta
|
||||
// Will not generate mutations!
|
||||
self.reclaim_attributes(node);
|
||||
|
@ -903,7 +878,7 @@ impl<'b> VirtualDom {
|
|||
self.reclaim_vnode_id(node);
|
||||
}
|
||||
|
||||
fn reclaim_vnode_id(&mut self, node: &'b VNode<'b>) {
|
||||
fn reclaim_vnode_id(&mut self, node: &'b VNode) {
|
||||
// Clean up the vnode id
|
||||
if let Some(id) = node.stable_id() {
|
||||
self.element_refs.remove(id.0);
|
||||
|
@ -1025,7 +1000,7 @@ impl<'b> VirtualDom {
|
|||
self.drop_scope(scope, false);
|
||||
}
|
||||
|
||||
fn find_first_element(&self, node: &'b VNode<'b>) -> ElementId {
|
||||
fn find_first_element(&self, node: &'b VNode) -> ElementId {
|
||||
match node.dynamic_root(0) {
|
||||
None => node.root_ids.borrow()[0],
|
||||
Some(Text(t)) => t.id.get().unwrap(),
|
||||
|
@ -1046,7 +1021,7 @@ impl<'b> VirtualDom {
|
|||
}
|
||||
}
|
||||
|
||||
fn find_last_element(&self, node: &'b VNode<'b>) -> ElementId {
|
||||
fn find_last_element(&self, node: &'b VNode) -> ElementId {
|
||||
match node.dynamic_root(node.template.get().roots.len() - 1) {
|
||||
None => *node.root_ids.borrow().last().unwrap(),
|
||||
Some(Text(t)) => t.id.get().unwrap(),
|
||||
|
@ -1067,7 +1042,7 @@ impl<'b> VirtualDom {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn assign_boundary_ref(&mut self, parent: Option<ElementRef>, child: &'b VNode<'b>) {
|
||||
pub(crate) fn assign_boundary_ref(&mut self, parent: Option<ElementRef>, child: &'b VNode) {
|
||||
if let Some(parent) = parent {
|
||||
// assign the parent of the child
|
||||
child.parent.set(Some(parent));
|
||||
|
@ -1080,7 +1055,7 @@ impl<'b> VirtualDom {
|
|||
/// We need to check for the obvious case, and the non-obvious case where the template as cloned
|
||||
///
|
||||
/// We use the pointer of the dynamic_node list in this case
|
||||
fn templates_are_the_same<'b>(left_template: &'b VNode<'b>, right_template: &'b VNode<'b>) -> bool {
|
||||
fn templates_are_the_same<'b>(left_template: &'b VNode, right_template: &'b VNode) -> bool {
|
||||
std::ptr::eq(left_template, right_template)
|
||||
}
|
||||
|
||||
|
@ -1092,9 +1067,9 @@ fn templates_are_different(left_template: &VNode, right_template: &VNode) -> boo
|
|||
}
|
||||
|
||||
fn matching_components<'a>(
|
||||
left: &'a VNode<'a>,
|
||||
right: &'a VNode<'a>,
|
||||
) -> Option<Vec<(&'a VComponent<'a>, &'a VComponent<'a>)>> {
|
||||
left: &'a VNode,
|
||||
right: &'a VNode,
|
||||
) -> Option<Vec<(&'a VComponent, &'a VComponent)>> {
|
||||
let left_template = left.template.get();
|
||||
let right_template = right.template.get();
|
||||
if left_template.roots.len() != right_template.roots.len() {
|
||||
|
|
|
@ -262,7 +262,7 @@ impl<T> Throw for Option<T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct ErrorHandler<'a>(Box<dyn Fn(CapturedError) -> LazyNodes<'a, 'a> + 'a>);
|
||||
pub struct ErrorHandler(Box<dyn Fn(CapturedError) -> LazyNodes<'a, 'a>>);
|
||||
impl<'a, F: Fn(CapturedError) -> LazyNodes<'a, 'a> + 'a> From<F> for ErrorHandler<'a> {
|
||||
fn from(value: F) -> Self {
|
||||
Self(Box::new(value))
|
||||
|
@ -290,7 +290,7 @@ fn default_handler<'a>(error: CapturedError) -> LazyNodes<'a, 'a> {
|
|||
stable_id: Default::default(),
|
||||
key: None,
|
||||
template: std::cell::Cell::new(TEMPLATE),
|
||||
root_ids: bumpalo::collections::Vec::with_capacity_in(1usize, __cx.bump()).into(),
|
||||
root_ids: Vec::with_capacity(1usize).into(),
|
||||
dynamic_nodes: __cx
|
||||
.bump()
|
||||
.alloc([__cx.text_node(format_args!("{0}", error))]),
|
||||
|
@ -298,45 +298,39 @@ fn default_handler<'a>(error: CapturedError) -> LazyNodes<'a, 'a> {
|
|||
}
|
||||
})
|
||||
}
|
||||
pub struct ErrorBoundaryProps<'a> {
|
||||
children: Element<'a>,
|
||||
handle_error: ErrorHandler<'a>,
|
||||
pub struct ErrorBoundaryProps {
|
||||
children: Element,
|
||||
handle_error: ErrorHandler,
|
||||
}
|
||||
impl<'a> ErrorBoundaryProps<'a> {
|
||||
impl ErrorBoundaryProps {
|
||||
/**
|
||||
Create a builder for building `ErrorBoundaryProps`.
|
||||
On the builder, call `.children(...)`(optional), `.handle_error(...)`(optional) to set the values of the fields.
|
||||
Finally, call `.build()` to create the instance of `ErrorBoundaryProps`.
|
||||
*/
|
||||
#[allow(dead_code)]
|
||||
pub fn builder() -> ErrorBoundaryPropsBuilder<'a, ((), ())> {
|
||||
ErrorBoundaryPropsBuilder {
|
||||
fields: ((), ()),
|
||||
_phantom: ::core::default::Default::default(),
|
||||
}
|
||||
pub fn builder() -> ErrorBoundaryPropsBuilder<((), ())> {
|
||||
ErrorBoundaryPropsBuilder { fields: ((), ()) }
|
||||
}
|
||||
}
|
||||
#[must_use]
|
||||
#[doc(hidden)]
|
||||
#[allow(dead_code, non_camel_case_types, non_snake_case)]
|
||||
pub struct ErrorBoundaryPropsBuilder<'a, TypedBuilderFields> {
|
||||
pub struct ErrorBoundaryPropsBuilder<TypedBuilderFields> {
|
||||
fields: TypedBuilderFields,
|
||||
_phantom: ::core::marker::PhantomData<&'a ()>,
|
||||
}
|
||||
impl<'a, TypedBuilderFields> Clone for ErrorBoundaryPropsBuilder<'a, TypedBuilderFields>
|
||||
impl<TypedBuilderFields> Clone for ErrorBoundaryPropsBuilder<TypedBuilderFields>
|
||||
where
|
||||
TypedBuilderFields: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
fields: self.fields.clone(),
|
||||
_phantom: ::core::default::Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'a> Properties for ErrorBoundaryProps<'a> {
|
||||
type Builder = ErrorBoundaryPropsBuilder<'a, ((), ())>;
|
||||
const IS_STATIC: bool = false;
|
||||
impl Properties for ErrorBoundaryProps {
|
||||
type Builder = ErrorBoundaryPropsBuilder<((), ())>;
|
||||
fn builder() -> Self::Builder {
|
||||
ErrorBoundaryProps::builder()
|
||||
}
|
||||
|
|
|
@ -147,7 +147,7 @@ impl<T> Default for EventHandler<'_, T> {
|
|||
}
|
||||
}
|
||||
|
||||
type ExternalListenerCallback<'bump, T> = bumpalo::boxed::Box<'bump, dyn FnMut(T) + 'bump>;
|
||||
type ExternalListenerCallback<'bump, T> = Box<dyn FnMut(T)>;
|
||||
|
||||
impl<T> EventHandler<'_, T> {
|
||||
/// Call this event handler with the appropriate event type
|
||||
|
|
|
@ -26,7 +26,7 @@ use crate::innerlude::*;
|
|||
///
|
||||
/// You want to use this free-function when your fragment needs a key and simply returning multiple nodes from rsx! won't cut it.
|
||||
#[allow(non_upper_case_globals, non_snake_case)]
|
||||
pub fn Fragment<'a>(cx: Scope<'a, FragmentProps<'a>>) -> Element {
|
||||
pub fn Fragment<'a>(cx: Scope<'a, FragmentProps>) -> Element {
|
||||
let children = cx.props.0.as_ref()?;
|
||||
Some(VNode {
|
||||
key: children.key,
|
||||
|
@ -92,8 +92,8 @@ impl<const A: bool> FragmentBuilder<A> {
|
|||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
impl<'a> Properties for FragmentProps<'a> {
|
||||
type Builder = FragmentBuilder<'a, false>;
|
||||
impl<'a> Properties for FragmentProps {
|
||||
type Builder = FragmentBuilder<false>;
|
||||
fn builder() -> Self::Builder {
|
||||
FragmentBuilder(None)
|
||||
}
|
||||
|
|
|
@ -1,101 +0,0 @@
|
|||
//! Support for storing lazy-nodes on the stack
|
||||
//!
|
||||
//! This module provides support for a type called `LazyNodes` which is a micro-heap located on the stack to make calls
|
||||
//! to `rsx!` more efficient.
|
||||
//!
|
||||
//! To support returning rsx! from branches in match statements, we need to use dynamic dispatch on [`ScopeState`] closures.
|
||||
//!
|
||||
//! This can be done either through boxing directly, or by using dynamic-sized-types and a custom allocator. In our case,
|
||||
//! we build a tiny alloactor in the stack and allocate the closure into that.
|
||||
//!
|
||||
//! The logic for this was borrowed from <https://docs.rs/stack_dst/0.6.1/stack_dst/>. Unfortunately, this crate does not
|
||||
//! support non-static closures, so we've implemented the core logic of `ValueA` in this module.
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use smallbox::{smallbox, space::S16, SmallBox};
|
||||
|
||||
use crate::{innerlude::VNode, ScopeState};
|
||||
|
||||
/// A concrete type provider for closures that build [`VNode`] structures.
|
||||
///
|
||||
/// This struct wraps lazy structs that build [`VNode`] trees. Normally, we cannot perform a blanket implementation over
|
||||
/// closures, but if we wrap the closure in a concrete type, we can use it for different branches in matching.
|
||||
///
|
||||
///
|
||||
/// ```rust, ignore
|
||||
/// LazyNodes::new(|f| {
|
||||
/// static TEMPLATE: dioxus::core::Template = dioxus::core::Template {
|
||||
/// name: "main.rs:5:5:20", // Source location of the template for hot reloading
|
||||
/// roots: &[
|
||||
/// dioxus::core::TemplateNode::Element {
|
||||
/// tag: dioxus_elements::div::TAG_NAME,
|
||||
/// namespace: dioxus_elements::div::NAME_SPACE,
|
||||
/// attrs: &[],
|
||||
/// children: &[],
|
||||
/// },
|
||||
/// ],
|
||||
/// node_paths: &[],
|
||||
/// attr_paths: &[],
|
||||
/// };
|
||||
/// dioxus::core::VNode {
|
||||
/// parent: None,
|
||||
/// key: None,
|
||||
/// template: std::cell::Cell::new(TEMPLATE),
|
||||
/// root_ids: dioxus::core::exports::bumpalo::collections::Vec::with_capacity_in(
|
||||
/// 1usize,
|
||||
/// f.bump(),
|
||||
/// )
|
||||
/// .into(),
|
||||
/// dynamic_nodes: f.bump().alloc([]),
|
||||
/// dynamic_attrs: f.bump().alloc([]),
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Find more information about how to construct [`VNode`] at <https://dioxuslabs.com/learn/0.4/contributing/walkthrough_readme#the-rsx-macro>
|
||||
|
||||
pub struct LazyNodes<'a, 'b> {
|
||||
#[cfg(not(miri))]
|
||||
inner: SmallBox<dyn FnMut(&'a ScopeState) -> VNode<'a> + 'b, S16>,
|
||||
|
||||
#[cfg(miri)]
|
||||
inner: Box<dyn FnMut(&'a ScopeState) -> VNode<'a> + 'b>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> LazyNodes<'a, 'b> {
|
||||
/// Create a new [`LazyNodes`] closure, optimistically placing it onto the stack.
|
||||
///
|
||||
/// If the closure cannot fit into the stack allocation (16 bytes), then it
|
||||
/// is placed on the heap. Most closures will fit into the stack, and is
|
||||
/// the most optimal way to use the creation function.
|
||||
pub fn new(val: impl FnOnce(&'a ScopeState) -> VNode<'a> + 'b) -> Self {
|
||||
// there's no way to call FnOnce without a box, so we need to store it in a slot and use static dispatch
|
||||
let mut slot = Some(val);
|
||||
|
||||
Self {
|
||||
#[cfg(not(miri))]
|
||||
inner: smallbox!(move |f| {
|
||||
let val = slot.take().expect("cannot call LazyNodes twice");
|
||||
val(f)
|
||||
}),
|
||||
|
||||
#[cfg(miri)]
|
||||
inner: Box::new(move |f| {
|
||||
let val = slot.take().expect("cannot call LazyNodes twice");
|
||||
val(f)
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Call the closure with the given factory to produce real [`VNode`].
|
||||
///
|
||||
/// ```rust, ignore
|
||||
/// let f = LazyNodes::new(/* Closure for creating VNodes */);
|
||||
///
|
||||
/// let node = f.call(cac);
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn call(mut self, f: &'a ScopeState) -> VNode<'a> {
|
||||
(self.inner)(f)
|
||||
}
|
||||
}
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
mod any_props;
|
||||
mod arena;
|
||||
mod bump_frame;
|
||||
mod create;
|
||||
mod diff;
|
||||
mod dirty_scope;
|
||||
|
@ -43,7 +42,7 @@ pub(crate) mod innerlude {
|
|||
/// An [`Element`] is a possibly-none [`VNode`] created by calling `render` on [`Scope`] or [`ScopeState`].
|
||||
///
|
||||
/// An Errored [`Element`] will propagate the error to the nearest error boundary.
|
||||
pub type Element<'a> = Option<VNode<'a>>;
|
||||
pub type Element = Option<VNode>;
|
||||
|
||||
/// A [`Component`] is a function that takes a [`Scope`] and returns an [`Element`].
|
||||
///
|
||||
|
@ -97,9 +96,3 @@ pub mod prelude {
|
|||
VNode, VirtualDom,
|
||||
};
|
||||
}
|
||||
|
||||
pub mod exports {
|
||||
//! Important dependencies that are used by the rest of the library
|
||||
//! Feel free to just add the dependencies in your own Crates.toml
|
||||
pub use bumpalo;
|
||||
}
|
||||
|
|
|
@ -19,9 +19,9 @@ pub type TemplateId = &'static str;
|
|||
///
|
||||
/// Dioxus will do its best to immediately resolve any async components into a regular Element, but as an implementor
|
||||
/// you might need to handle the case where there's no node immediately ready.
|
||||
pub enum RenderReturn<'a> {
|
||||
pub enum RenderReturn {
|
||||
/// A currently-available element
|
||||
Ready(VNode<'a>),
|
||||
Ready(VNode),
|
||||
|
||||
/// The component aborted rendering early. It might've thrown an error.
|
||||
///
|
||||
|
@ -30,7 +30,7 @@ pub enum RenderReturn<'a> {
|
|||
Aborted(VPlaceholder),
|
||||
}
|
||||
|
||||
impl<'a> Default for RenderReturn<'a> {
|
||||
impl Default for RenderReturn {
|
||||
fn default() -> Self {
|
||||
RenderReturn::Aborted(VPlaceholder::default())
|
||||
}
|
||||
|
@ -88,11 +88,11 @@ impl VNode {
|
|||
|
||||
/// Create a new VNode
|
||||
pub fn new(
|
||||
key: Option<&'a str>,
|
||||
key: Option<String>,
|
||||
template: Template<'static>,
|
||||
root_ids: bumpalo::collections::Vec<'a, ElementId>,
|
||||
dynamic_nodes: &'a [DynamicNode<'a>],
|
||||
dynamic_attrs: &'a [Attribute<'a>],
|
||||
root_ids: Vec<ElementId>,
|
||||
dynamic_nodes: &'a [DynamicNode],
|
||||
dynamic_attrs: &'a [Attribute],
|
||||
) -> Self {
|
||||
Self {
|
||||
key,
|
||||
|
@ -113,7 +113,7 @@ impl VNode {
|
|||
/// Load a dynamic root at the given index
|
||||
///
|
||||
/// Returns [`None`] if the root is actually a static node (Element/Text)
|
||||
pub fn dynamic_root(&self, idx: usize) -> Option<&'a DynamicNode<'a>> {
|
||||
pub fn dynamic_root(&self, idx: usize) -> Option<&'a DynamicNode> {
|
||||
match &self.template.get().roots[idx] {
|
||||
TemplateNode::Element { .. } | TemplateNode::Text { text: _ } => None,
|
||||
TemplateNode::Dynamic { id } | TemplateNode::DynamicText { id } => {
|
||||
|
@ -147,7 +147,7 @@ pub struct Template<'a> {
|
|||
///
|
||||
/// Unlike react, calls to `rsx!` can have multiple roots. This list supports that paradigm.
|
||||
#[cfg_attr(feature = "serialize", serde(deserialize_with = "deserialize_leaky"))]
|
||||
pub roots: &'a [TemplateNode<'a>],
|
||||
pub roots: &'a [TemplateNode],
|
||||
|
||||
/// The paths of each node relative to the root of the template.
|
||||
///
|
||||
|
@ -242,7 +242,7 @@ impl<'a> Template<'a> {
|
|||
derive(serde::Serialize, serde::Deserialize),
|
||||
serde(tag = "type")
|
||||
)]
|
||||
pub enum TemplateNode<'a> {
|
||||
pub enum TemplateNode {
|
||||
/// An statically known element in the dom.
|
||||
///
|
||||
/// In HTML this would be something like `<div id="123"> </div>`
|
||||
|
@ -250,7 +250,7 @@ pub enum TemplateNode<'a> {
|
|||
/// The name of the element
|
||||
///
|
||||
/// IE for a div, it would be the string "div"
|
||||
tag: &'a str,
|
||||
tag: &'static str,
|
||||
|
||||
/// The namespace of the element
|
||||
///
|
||||
|
@ -260,23 +260,23 @@ pub enum TemplateNode<'a> {
|
|||
feature = "serialize",
|
||||
serde(deserialize_with = "deserialize_option_leaky")
|
||||
)]
|
||||
namespace: Option<&'a str>,
|
||||
namespace: Option<&'static str>,
|
||||
|
||||
/// A list of possibly dynamic attribues for this element
|
||||
///
|
||||
/// An attribute on a DOM node, such as `id="my-thing"` or `href="https://example.com"`.
|
||||
#[cfg_attr(feature = "serialize", serde(deserialize_with = "deserialize_leaky"))]
|
||||
attrs: &'a [TemplateAttribute<'a>],
|
||||
attrs: &'static [TemplateAttribute],
|
||||
|
||||
/// A list of template nodes that define another set of template nodes
|
||||
#[cfg_attr(feature = "serialize", serde(deserialize_with = "deserialize_leaky"))]
|
||||
children: &'a [TemplateNode<'a>],
|
||||
children: &'static [TemplateNode],
|
||||
},
|
||||
|
||||
/// This template node is just a piece of static text
|
||||
Text {
|
||||
/// The actual text
|
||||
text: &'a str,
|
||||
text: &'static str,
|
||||
},
|
||||
|
||||
/// This template node is unknown, and needs to be created at runtime.
|
||||
|
@ -298,7 +298,7 @@ pub enum TemplateNode<'a> {
|
|||
///
|
||||
/// This node's index in the DynamicNode list on VNode should match its repsective `Dynamic` index
|
||||
#[derive(Debug)]
|
||||
pub enum DynamicNode<'a> {
|
||||
pub enum DynamicNode {
|
||||
/// A component node
|
||||
///
|
||||
/// Most of the time, Dioxus will actually know which component this is as compile time, but the props and
|
||||
|
@ -306,10 +306,10 @@ pub enum DynamicNode<'a> {
|
|||
///
|
||||
/// The actual VComponent can be dynamic between two VNodes, though, allowing implementations to swap
|
||||
/// the render function at runtime
|
||||
Component(VComponent<'a>),
|
||||
Component(VComponent),
|
||||
|
||||
/// A text node
|
||||
Text(VText<'a>),
|
||||
Text(VText),
|
||||
|
||||
/// A placeholder
|
||||
///
|
||||
|
@ -322,27 +322,20 @@ pub enum DynamicNode<'a> {
|
|||
///
|
||||
/// Note that this is not a list of dynamic nodes. These must be VNodes and created through conditional rendering
|
||||
/// or iterators.
|
||||
Fragment(&'a [VNode<'a>]),
|
||||
Fragment(&'static [VNode]),
|
||||
}
|
||||
|
||||
impl Default for DynamicNode<'_> {
|
||||
impl Default for DynamicNode {
|
||||
fn default() -> Self {
|
||||
Self::Placeholder(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
/// An instance of a child component
|
||||
pub struct VComponent<'a> {
|
||||
pub struct VComponent {
|
||||
/// The name of this component
|
||||
pub name: &'static str,
|
||||
|
||||
/// Are the props valid for the 'static lifetime?
|
||||
///
|
||||
/// Internally, this is used as a guarantee. Externally, this might be incorrect, so don't count on it.
|
||||
///
|
||||
/// This flag is assumed by the [`crate::Properties`] trait which is unsafe to implement
|
||||
pub(crate) static_props: bool,
|
||||
|
||||
/// The assigned Scope for this component
|
||||
pub(crate) scope: Cell<Option<ScopeId>>,
|
||||
|
||||
|
@ -351,17 +344,17 @@ pub struct VComponent<'a> {
|
|||
/// It is possible that components get folded at compile time, so these shouldn't be really used as a key
|
||||
pub(crate) render_fn: *const (),
|
||||
|
||||
pub(crate) props: RefCell<Option<Box<dyn AnyProps<'a> + 'a>>>,
|
||||
pub(crate) props: RefCell<Option<Box<dyn AnyProps>>>,
|
||||
}
|
||||
|
||||
impl<'a> VComponent<'a> {
|
||||
impl<'a> VComponent {
|
||||
/// Get the scope that this component is mounted to
|
||||
pub fn mounted_scope(&self) -> Option<ScopeId> {
|
||||
self.scope.get()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> std::fmt::Debug for VComponent<'a> {
|
||||
impl<'a> std::fmt::Debug for VComponent {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("VComponent")
|
||||
.field("name", &self.name)
|
||||
|
@ -373,17 +366,17 @@ impl<'a> std::fmt::Debug for VComponent<'a> {
|
|||
|
||||
/// An instance of some text, mounted to the DOM
|
||||
#[derive(Debug)]
|
||||
pub struct VText<'a> {
|
||||
pub struct VText {
|
||||
/// The actual text itself
|
||||
pub value: &'a str,
|
||||
pub value: String,
|
||||
|
||||
/// The ID of this node in the real DOM
|
||||
pub(crate) id: Cell<Option<ElementId>>,
|
||||
}
|
||||
|
||||
impl<'a> VText<'a> {
|
||||
impl<'a> VText {
|
||||
/// Create a new VText
|
||||
pub fn new(value: &'a str) -> Self {
|
||||
pub fn new(value: String) -> Self {
|
||||
Self {
|
||||
value,
|
||||
id: Default::default(),
|
||||
|
@ -419,21 +412,21 @@ impl VPlaceholder {
|
|||
derive(serde::Serialize, serde::Deserialize),
|
||||
serde(tag = "type")
|
||||
)]
|
||||
pub enum TemplateAttribute<'a> {
|
||||
pub enum TemplateAttribute {
|
||||
/// This attribute is entirely known at compile time, enabling
|
||||
Static {
|
||||
/// The name of this attribute.
|
||||
///
|
||||
/// For example, the `href` attribute in `href="https://example.com"`, would have the name "href"
|
||||
name: &'a str,
|
||||
name: &'static str,
|
||||
|
||||
/// The value of this attribute, known at compile time
|
||||
///
|
||||
/// Currently this only accepts &str, so values, even if they're known at compile time, are not known
|
||||
value: &'a str,
|
||||
value: &'static str,
|
||||
|
||||
/// The namespace of this attribute. Does not exist in the HTML spec
|
||||
namespace: Option<&'a str>,
|
||||
namespace: Option<&'static str>,
|
||||
},
|
||||
|
||||
/// The attribute in this position is actually determined dynamically at runtime
|
||||
|
@ -447,12 +440,12 @@ pub enum TemplateAttribute<'a> {
|
|||
|
||||
/// An attribute on a DOM node, such as `id="my-thing"` or `href="https://example.com"`
|
||||
#[derive(Debug)]
|
||||
pub struct Attribute<'a> {
|
||||
pub struct Attribute {
|
||||
/// The name of the attribute.
|
||||
pub name: &'a str,
|
||||
pub name: &'static str,
|
||||
|
||||
/// The value of the attribute
|
||||
pub value: AttributeValue<'a>,
|
||||
pub value: AttributeValue,
|
||||
|
||||
/// The namespace of the attribute.
|
||||
///
|
||||
|
@ -466,11 +459,11 @@ pub struct Attribute<'a> {
|
|||
pub(crate) mounted_element: Cell<ElementId>,
|
||||
}
|
||||
|
||||
impl<'a> Attribute<'a> {
|
||||
impl Attribute {
|
||||
/// Create a new attribute
|
||||
pub fn new(
|
||||
name: &'a str,
|
||||
value: AttributeValue<'a>,
|
||||
name: &'static str,
|
||||
value: AttributeValue,
|
||||
namespace: Option<&'static str>,
|
||||
volatile: bool,
|
||||
) -> Self {
|
||||
|
@ -493,9 +486,9 @@ impl<'a> Attribute<'a> {
|
|||
///
|
||||
/// These are built-in to be faster during the diffing process. To use a custom value, use the [`AttributeValue::Any`]
|
||||
/// variant.
|
||||
pub enum AttributeValue<'a> {
|
||||
pub enum AttributeValue {
|
||||
/// Text attribute
|
||||
Text(&'a str),
|
||||
Text(String),
|
||||
|
||||
/// A float
|
||||
Float(f64),
|
||||
|
@ -507,16 +500,16 @@ pub enum AttributeValue<'a> {
|
|||
Bool(bool),
|
||||
|
||||
/// A listener, like "onclick"
|
||||
Listener(RefCell<Option<ListenerCb<'a>>>),
|
||||
Listener(RefCell<Option<ListenerCb>>),
|
||||
|
||||
/// An arbitrary value that implements PartialEq and is static
|
||||
Any(RefCell<Option<BumpBox<'a, dyn AnyValue>>>),
|
||||
Any(RefCell<Option<Box<dyn AnyValue>>>),
|
||||
|
||||
/// A "none" value, resulting in the removal of an attribute from the dom
|
||||
None,
|
||||
}
|
||||
|
||||
pub type ListenerCb<'a> = BumpBox<'a, dyn FnMut(Event<dyn Any>) + 'a>;
|
||||
pub type ListenerCb = Box<dyn FnMut(Event<dyn Any>)>;
|
||||
|
||||
/// Any of the built-in values that the Dioxus VirtualDom supports as dynamic attributes on elements that are borrowed
|
||||
///
|
||||
|
@ -550,8 +543,8 @@ pub enum BorrowedAttributeValue<'a> {
|
|||
None,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a AttributeValue<'a>> for BorrowedAttributeValue<'a> {
|
||||
fn from(value: &'a AttributeValue<'a>) -> Self {
|
||||
impl<'a> From<&'a AttributeValue> for BorrowedAttributeValue<'a> {
|
||||
fn from(value: &'a AttributeValue) -> Self {
|
||||
match value {
|
||||
AttributeValue::Text(value) => BorrowedAttributeValue::Text(value),
|
||||
AttributeValue::Float(value) => BorrowedAttributeValue::Float(*value),
|
||||
|
@ -613,7 +606,7 @@ where
|
|||
panic!("Any cannot be deserialized")
|
||||
}
|
||||
|
||||
impl<'a> std::fmt::Debug for AttributeValue<'a> {
|
||||
impl std::fmt::Debug for AttributeValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Text(arg0) => f.debug_tuple("Text").field(arg0).finish(),
|
||||
|
@ -627,7 +620,7 @@ impl<'a> std::fmt::Debug for AttributeValue<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq for AttributeValue<'a> {
|
||||
impl PartialEq for AttributeValue {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(Self::Text(l0), Self::Text(r0)) => l0 == r0,
|
||||
|
@ -668,42 +661,33 @@ impl<T: Any + PartialEq + 'static> AnyValue for T {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> RenderReturn<'a> {
|
||||
pub(crate) unsafe fn extend_lifetime_ref<'c>(&self) -> &'c RenderReturn<'c> {
|
||||
unsafe { std::mem::transmute(self) }
|
||||
}
|
||||
pub(crate) unsafe fn extend_lifetime<'c>(self) -> RenderReturn<'c> {
|
||||
unsafe { std::mem::transmute(self) }
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait that allows various items to be converted into a dynamic node for the rsx macro
|
||||
pub trait IntoDynNode<'a, A = ()> {
|
||||
/// Consume this item along with a scopestate and produce a DynamicNode
|
||||
///
|
||||
/// You can use the bump alloactor of the scopestate to creat the dynamic node
|
||||
fn into_dyn_node(self, cx: &'a ScopeState) -> DynamicNode<'a>;
|
||||
fn into_dyn_node(self, cx: &'a ScopeState) -> DynamicNode;
|
||||
}
|
||||
|
||||
impl<'a> IntoDynNode<'a> for () {
|
||||
fn into_dyn_node(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
|
||||
fn into_dyn_node(self, _cx: &'a ScopeState) -> DynamicNode {
|
||||
DynamicNode::default()
|
||||
}
|
||||
}
|
||||
impl<'a> IntoDynNode<'a> for VNode<'a> {
|
||||
fn into_dyn_node(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
|
||||
impl<'a> IntoDynNode<'a> for VNode {
|
||||
fn into_dyn_node(self, _cx: &'a ScopeState) -> DynamicNode {
|
||||
DynamicNode::Fragment(_cx.bump().alloc([self]))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoDynNode<'a> for DynamicNode<'a> {
|
||||
fn into_dyn_node(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
|
||||
impl<'a> IntoDynNode<'a> for DynamicNode {
|
||||
fn into_dyn_node(self, _cx: &'a ScopeState) -> DynamicNode {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: IntoDynNode<'a>> IntoDynNode<'a> for Option<T> {
|
||||
fn into_dyn_node(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
|
||||
fn into_dyn_node(self, _cx: &'a ScopeState) -> DynamicNode {
|
||||
match self {
|
||||
Some(val) => val.into_dyn_node(_cx),
|
||||
None => DynamicNode::default(),
|
||||
|
@ -711,8 +695,8 @@ impl<'a, T: IntoDynNode<'a>> IntoDynNode<'a> for Option<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoDynNode<'a> for &Element<'a> {
|
||||
fn into_dyn_node(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
|
||||
impl<'a> IntoDynNode<'a> for &Element {
|
||||
fn into_dyn_node(self, _cx: &'a ScopeState) -> DynamicNode {
|
||||
match self.as_ref() {
|
||||
Some(val) => val.clone().into_dyn_node(_cx),
|
||||
_ => DynamicNode::default(),
|
||||
|
@ -721,13 +705,13 @@ impl<'a> IntoDynNode<'a> for &Element<'a> {
|
|||
}
|
||||
|
||||
impl<'a, 'b> IntoDynNode<'a> for LazyNodes<'a, 'b> {
|
||||
fn into_dyn_node(self, cx: &'a ScopeState) -> DynamicNode<'a> {
|
||||
fn into_dyn_node(self, cx: &'a ScopeState) -> DynamicNode {
|
||||
DynamicNode::Fragment(cx.bump().alloc([cx.render(self).unwrap()]))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> IntoDynNode<'b> for &'a str {
|
||||
fn into_dyn_node(self, cx: &'b ScopeState) -> DynamicNode<'b> {
|
||||
fn into_dyn_node(self, cx: &'b ScopeState) -> DynamicNode {
|
||||
DynamicNode::Text(VText {
|
||||
value: cx.bump().alloc_str(self),
|
||||
id: Default::default(),
|
||||
|
@ -745,13 +729,13 @@ impl IntoDynNode<'_> for String {
|
|||
}
|
||||
|
||||
impl<'b> IntoDynNode<'b> for Arguments<'_> {
|
||||
fn into_dyn_node(self, cx: &'b ScopeState) -> DynamicNode<'b> {
|
||||
fn into_dyn_node(self, cx: &'b ScopeState) -> DynamicNode {
|
||||
cx.text_node(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoDynNode<'a> for &'a VNode<'a> {
|
||||
fn into_dyn_node(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
|
||||
impl<'a> IntoDynNode<'a> for &'a VNode {
|
||||
fn into_dyn_node(self, _cx: &'a ScopeState) -> DynamicNode {
|
||||
DynamicNode::Fragment(_cx.bump().alloc([VNode {
|
||||
parent: self.parent.clone(),
|
||||
stable_id: self.stable_id.clone(),
|
||||
|
@ -764,24 +748,24 @@ impl<'a> IntoDynNode<'a> for &'a VNode<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait IntoVNode<'a> {
|
||||
fn into_vnode(self, _cx: &'a ScopeState) -> VNode<'a>;
|
||||
pub trait IntoVNode {
|
||||
fn into_vnode(self) -> VNode;
|
||||
}
|
||||
impl<'a> IntoVNode<'a> for VNode<'a> {
|
||||
fn into_vnode(self, _cx: &'a ScopeState) -> VNode<'a> {
|
||||
impl<'a> IntoVNode for VNode {
|
||||
fn into_vnode(self) -> VNode {
|
||||
self
|
||||
}
|
||||
}
|
||||
impl<'a> IntoVNode<'a> for Element<'a> {
|
||||
fn into_vnode(self, cx: &'a ScopeState) -> VNode<'a> {
|
||||
impl<'a> IntoVNode for Element {
|
||||
fn into_vnode(self) -> VNode {
|
||||
match self {
|
||||
Some(val) => val.into_vnode(cx),
|
||||
_ => VNode::empty(cx).unwrap(),
|
||||
_ => VNode::empty().unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'a, 'b> IntoVNode<'a> for LazyNodes<'a, 'b> {
|
||||
fn into_vnode(self, cx: &'a ScopeState) -> VNode<'a> {
|
||||
impl<'a, 'b> IntoVNode for LazyNodes<'a, 'b> {
|
||||
fn into_vnode(self) -> VNode {
|
||||
cx.render(self).unwrap()
|
||||
}
|
||||
}
|
||||
|
@ -791,9 +775,9 @@ pub struct FromNodeIterator;
|
|||
impl<'a, T, I> IntoDynNode<'a, FromNodeIterator> for T
|
||||
where
|
||||
T: Iterator<Item = I>,
|
||||
I: IntoVNode<'a>,
|
||||
I: IntoVNode,
|
||||
{
|
||||
fn into_dyn_node(self, cx: &'a ScopeState) -> DynamicNode<'a> {
|
||||
fn into_dyn_node(self, cx: &'a ScopeState) -> DynamicNode {
|
||||
let mut nodes = bumpalo::collections::Vec::new_in(cx.bump());
|
||||
|
||||
nodes.extend(self.into_iter().map(|node| node.into_vnode(cx)));
|
||||
|
|
|
@ -32,7 +32,7 @@ use crate::innerlude::*;
|
|||
/// data: &'a str
|
||||
/// }
|
||||
/// ```
|
||||
pub trait Properties: Sized + 'static {
|
||||
pub trait Properties: Clone + Sized + 'static {
|
||||
/// The type of the builder for this component.
|
||||
/// Used to create "in-progress" versions of the props.
|
||||
type Builder;
|
||||
|
@ -66,7 +66,7 @@ impl EmptyBuilder {
|
|||
|
||||
/// This utility function launches the builder method so rsx! and html! macros can use the typed-builder pattern
|
||||
/// to initialize a component's props.
|
||||
pub fn fc_to_builder<'a, T: Properties + 'a>(_: fn(Scope<'a, T>) -> Element<'a>) -> T::Builder {
|
||||
pub fn fc_to_builder<'a, T: Properties + 'a>(_: fn(Scope<'a, T>) -> Element) -> T::Builder {
|
||||
T::builder()
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use crate::{
|
||||
any_props::AnyProps,
|
||||
bump_frame::BumpFrame,
|
||||
innerlude::DirtyScope,
|
||||
nodes::RenderReturn,
|
||||
scope_context::ScopeContext,
|
||||
|
@ -11,7 +10,7 @@ use crate::{
|
|||
impl VirtualDom {
|
||||
pub(super) fn new_scope(
|
||||
&mut self,
|
||||
props: Box<dyn AnyProps<'static>>,
|
||||
props: Box<dyn AnyProps>,
|
||||
name: &'static str,
|
||||
) -> &ScopeState {
|
||||
let parent_id = self.runtime.current_scope_id();
|
||||
|
@ -27,9 +26,6 @@ impl VirtualDom {
|
|||
|
||||
props: Some(props),
|
||||
|
||||
node_arena_1: BumpFrame::new(0),
|
||||
node_arena_2: BumpFrame::new(0),
|
||||
|
||||
render_cnt: Default::default(),
|
||||
hooks: Default::default(),
|
||||
hook_idx: Default::default(),
|
||||
|
|
|
@ -301,7 +301,7 @@ pub fn provide_root_context<T: 'static + Clone>(value: T) -> Option<T> {
|
|||
}
|
||||
|
||||
/// Suspends the current component
|
||||
pub fn suspend() -> Option<Element<'static>> {
|
||||
pub fn suspend() -> Option<Element> {
|
||||
with_current_scope(|cx| {
|
||||
cx.suspend();
|
||||
});
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
use crate::{
|
||||
any_props::AnyProps,
|
||||
any_props::VProps,
|
||||
bump_frame::BumpFrame,
|
||||
innerlude::ErrorBoundary,
|
||||
innerlude::{DynamicNode, EventHandler, VComponent, VNodeId, VText},
|
||||
lazynodes::LazyNodes,
|
||||
nodes::{IntoAttributeValue, IntoDynNode, RenderReturn},
|
||||
runtime::Runtime,
|
||||
scope_context::ScopeContext,
|
||||
AnyValue, Attribute, AttributeValue, Element, Event, Properties, TaskId,
|
||||
};
|
||||
use bumpalo::{boxed::Box as BumpBox, Bump};
|
||||
use std::{
|
||||
any::Any,
|
||||
cell::{Cell, Ref, RefCell, UnsafeCell},
|
||||
|
@ -90,11 +87,11 @@ pub struct ScopeState {
|
|||
pub(crate) hooks: RefCell<Vec<Box<UnsafeCell<dyn Any>>>>,
|
||||
pub(crate) hook_idx: Cell<usize>,
|
||||
|
||||
pub(crate) borrowed_props: RefCell<Vec<*const VComponent<'static>>>,
|
||||
pub(crate) borrowed_props: RefCell<Vec<*const VComponent>>,
|
||||
pub(crate) element_refs_to_drop: RefCell<Vec<VNodeId>>,
|
||||
pub(crate) attributes_to_drop_before_render: RefCell<Vec<*const Attribute<'static>>>,
|
||||
pub(crate) attributes_to_drop_before_render: RefCell<Vec<*const Attribute>>,
|
||||
|
||||
pub(crate) props: Option<Box<dyn AnyProps<'static>>>,
|
||||
pub(crate) props: Option<Box<dyn AnyProps>>,
|
||||
}
|
||||
|
||||
impl Drop for ScopeState {
|
||||
|
@ -108,22 +105,6 @@ impl<'src> ScopeState {
|
|||
self.runtime.get_context(self.context_id).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn current_frame(&self) -> &BumpFrame {
|
||||
match self.render_cnt.get() % 2 {
|
||||
0 => &self.node_arena_1,
|
||||
1 => &self.node_arena_2,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn previous_frame(&self) -> &BumpFrame {
|
||||
match self.render_cnt.get() % 2 {
|
||||
1 => &self.node_arena_1,
|
||||
0 => &self.node_arena_2,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the name of this component
|
||||
pub fn name(&self) -> &str {
|
||||
self.context().name
|
||||
|
@ -136,17 +117,6 @@ impl<'src> ScopeState {
|
|||
self.render_cnt.get()
|
||||
}
|
||||
|
||||
/// Get a handle to the currently active bump arena for this Scope
|
||||
///
|
||||
/// This is a bump memory allocator. Be careful using this directly since the contents will be wiped on the next render.
|
||||
/// It's easy to leak memory here since the drop implementation will not be called for any objects allocated in this arena.
|
||||
///
|
||||
/// If you need to allocate items that need to be dropped, use bumpalo's box.
|
||||
pub fn bump(&self) -> &Bump {
|
||||
// note that this is actually the previous frame since we use that as scratch space while the component is rendering
|
||||
self.previous_frame().bump()
|
||||
}
|
||||
|
||||
/// Get a handle to the currently active head node arena for this Scope
|
||||
///
|
||||
/// This is useful for traversing the tree outside of the VirtualDom, such as in a custom renderer or in SSR.
|
||||
|
@ -343,7 +313,7 @@ impl<'src> ScopeState {
|
|||
/// cx.render(lazy_tree)
|
||||
/// }
|
||||
///```
|
||||
pub fn render(&'src self, rsx: LazyNodes<'src, '_>) -> Element<'src> {
|
||||
pub fn render(&'src self, rsx: LazyNodes<'src, '_>) -> Element {
|
||||
let element = rsx.call(self);
|
||||
|
||||
let mut listeners = self.attributes_to_drop_before_render.borrow_mut();
|
||||
|
@ -383,25 +353,13 @@ impl<'src> ScopeState {
|
|||
}
|
||||
|
||||
/// Create a dynamic text node using [`Arguments`] and the [`ScopeState`]'s internal [`Bump`] allocator
|
||||
pub fn text_node(&'src self, args: Arguments) -> DynamicNode<'src> {
|
||||
pub fn text_node(&'src self, args: Arguments) -> DynamicNode {
|
||||
DynamicNode::Text(VText {
|
||||
value: self.raw_text(args),
|
||||
id: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Allocate some text inside the [`ScopeState`] from [`Arguments`]
|
||||
///
|
||||
/// Uses the currently active [`Bump`] allocator
|
||||
pub fn raw_text(&'src self, args: Arguments) -> &'src str {
|
||||
args.as_str().unwrap_or_else(|| {
|
||||
use bumpalo::core_alloc::fmt::Write;
|
||||
let mut str_buf = bumpalo::collections::String::new_in(self.bump());
|
||||
str_buf.write_fmt(args).unwrap();
|
||||
str_buf.into_bump_str()
|
||||
})
|
||||
}
|
||||
|
||||
/// Convert any item that implements [`IntoDynNode`] into a [`DynamicNode`] using the internal [`Bump`] allocator
|
||||
pub fn make_node<'c, I>(&'src self, into: impl IntoDynNode<'src, I> + 'c) -> DynamicNode {
|
||||
into.into_dyn_node(self)
|
||||
|
@ -417,7 +375,7 @@ impl<'src> ScopeState {
|
|||
value: impl IntoAttributeValue<'src>,
|
||||
namespace: Option<&'static str>,
|
||||
volatile: bool,
|
||||
) -> Attribute<'src> {
|
||||
) -> Attribute {
|
||||
Attribute {
|
||||
name,
|
||||
namespace,
|
||||
|
@ -443,10 +401,10 @@ impl<'src> ScopeState {
|
|||
/// ```
|
||||
pub fn component<'child, P>(
|
||||
&'src self,
|
||||
component: fn(Scope<'child, P>) -> Element<'child>,
|
||||
component: fn(Scope<'child, P>) -> Element,
|
||||
props: P,
|
||||
fn_name: &'static str,
|
||||
) -> DynamicNode<'src>
|
||||
) -> DynamicNode
|
||||
where
|
||||
// The properties must be valid until the next bump frame
|
||||
P: Properties + 'src,
|
||||
|
@ -456,13 +414,12 @@ impl<'src> ScopeState {
|
|||
let vcomp = VProps::new(component, P::memoize, props);
|
||||
|
||||
// cast off the lifetime of the render return
|
||||
let as_dyn: Box<dyn AnyProps<'child> + '_> = Box::new(vcomp);
|
||||
let extended: Box<dyn AnyProps<'src> + 'src> = unsafe { std::mem::transmute(as_dyn) };
|
||||
let as_dyn: Box<dyn AnyProps + '_> = Box::new(vcomp);
|
||||
let extended: Box<dyn AnyProps + 'src> = unsafe { std::mem::transmute(as_dyn) };
|
||||
|
||||
DynamicNode::Component(VComponent {
|
||||
name: fn_name,
|
||||
render_fn: component as *const (),
|
||||
static_props: P::IS_STATIC,
|
||||
props: RefCell::new(Some(extended)),
|
||||
scope: Default::default(),
|
||||
})
|
||||
|
@ -470,9 +427,9 @@ impl<'src> ScopeState {
|
|||
|
||||
/// Create a new [`EventHandler`] from an [`FnMut`]
|
||||
pub fn event_handler<T>(&'src self, f: impl FnMut(T) + 'src) -> EventHandler<'src, T> {
|
||||
let handler: &mut dyn FnMut(T) = self.bump().alloc(f);
|
||||
let caller = unsafe { BumpBox::from_raw(handler as *mut dyn FnMut(T)) };
|
||||
let callback = RefCell::new(Some(caller));
|
||||
let callback = RefCell::new(Some(Box::new(move |event: Event<T>| {
|
||||
f(event.data);
|
||||
})));
|
||||
EventHandler {
|
||||
callback,
|
||||
origin: self.context().id,
|
||||
|
@ -485,34 +442,22 @@ impl<'src> ScopeState {
|
|||
pub fn listener<T: 'static>(
|
||||
&'src self,
|
||||
mut callback: impl FnMut(Event<T>) + 'src,
|
||||
) -> AttributeValue<'src> {
|
||||
// safety: there's no other way to create a dynamicly-dispatched bump box other than alloc + from-raw
|
||||
// This is the suggested way to build a bumpbox
|
||||
//
|
||||
// In theory, we could just use regular boxes
|
||||
let boxed: BumpBox<'src, dyn FnMut(_) + 'src> = unsafe {
|
||||
BumpBox::from_raw(self.bump().alloc(move |event: Event<dyn Any>| {
|
||||
) -> AttributeValue {
|
||||
AttributeValue::Listener(RefCell::new(Some(Box::new(
|
||||
move |event: Event<dyn Any>| {
|
||||
if let Ok(data) = event.data.downcast::<T>() {
|
||||
callback(Event {
|
||||
propagates: event.propagates,
|
||||
data,
|
||||
});
|
||||
}
|
||||
}))
|
||||
};
|
||||
|
||||
AttributeValue::Listener(RefCell::new(Some(boxed)))
|
||||
},
|
||||
))))
|
||||
}
|
||||
|
||||
/// Create a new [`AttributeValue`] with a value that implements [`AnyValue`]
|
||||
pub fn any_value<T: AnyValue>(&'src self, value: T) -> AttributeValue<'src> {
|
||||
// safety: there's no other way to create a dynamicly-dispatched bump box other than alloc + from-raw
|
||||
// This is the suggested way to build a bumpbox
|
||||
//
|
||||
// In theory, we could just use regular boxes
|
||||
let boxed: BumpBox<'src, dyn AnyValue> =
|
||||
unsafe { BumpBox::from_raw(self.bump().alloc(value)) };
|
||||
AttributeValue::Any(RefCell::new(Some(boxed)))
|
||||
pub fn any_value<T: AnyValue>(&'src self, value: T) -> AttributeValue {
|
||||
AttributeValue::Any(RefCell::new(Some(Box::new(value))))
|
||||
}
|
||||
|
||||
/// Mark this component as suspended and then return None
|
||||
|
|
|
@ -16,7 +16,9 @@ use crate::{
|
|||
use futures_util::{pin_mut, StreamExt};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use slab::Slab;
|
||||
use std::{any::Any, cell::Cell, collections::BTreeSet, future::Future, ptr::NonNull, 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 +188,7 @@ 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) element_refs: Slab<Option<NonNull<VNode<'static>>>>,
|
||||
pub(crate) element_refs: Slab<Option<NonNull<VNode>>>,
|
||||
|
||||
// The element ids that are used in the renderer
|
||||
pub(crate) elements: Slab<Option<ElementRef>>,
|
||||
|
|
|
@ -9,7 +9,7 @@ macro_rules! impl_event {
|
|||
$(
|
||||
$( #[$attr] )*
|
||||
#[inline]
|
||||
pub fn $name<'a, E: crate::EventReturn<T>, T>(_cx: &'a ::dioxus_core::ScopeState, mut _f: impl FnMut(::dioxus_core::Event<$data>) -> E + 'a) -> ::dioxus_core::Attribute<'a> {
|
||||
pub fn $name<'a, E: crate::EventReturn<T>, T>(_cx: &'a ::dioxus_core::ScopeState, mut _f: impl FnMut(::dioxus_core::Event<$data>) -> E + 'a) -> ::dioxus_core::Attribute {
|
||||
::dioxus_core::Attribute::new(
|
||||
stringify!($name),
|
||||
_cx.listener(move |e: ::dioxus_core::Event<$data>| {
|
||||
|
|
|
@ -151,7 +151,7 @@ impl_event! {
|
|||
pub fn ondoubleclick<'a, E: crate::EventReturn<T>, T>(
|
||||
_cx: &'a ::dioxus_core::ScopeState,
|
||||
mut _f: impl FnMut(::dioxus_core::Event<MouseData>) -> E + 'a,
|
||||
) -> ::dioxus_core::Attribute<'a> {
|
||||
) -> ::dioxus_core::Attribute {
|
||||
::dioxus_core::Attribute::new(
|
||||
"ondblclick",
|
||||
_cx.listener(move |e: ::dioxus_core::Event<MouseData>| {
|
||||
|
|
Loading…
Reference in a new issue