wip: clean up some things
This commit is contained in:
1 parent
ba26b1001a
commit
3b166c9edd
64 files changed
+540
-599
No files matched your search
+23
-23
@@ -228,27 +228,29 @@ impl<'b> VirtualDom {
|
||||
self.create_static_node(node);
|
||||
}
|
||||
|
||||
self.mutations.template_mutations.push(SaveTemplate {
|
||||
self.mutations.template_edits.push(SaveTemplate {
|
||||
name: template.template.id,
|
||||
m: template.template.roots.len(),
|
||||
});
|
||||
}
|
||||
|
||||
// todo: we shouldn't have any of instructions for building templates - renderers should be able to work with the
|
||||
// template type directly, right?
|
||||
pub(crate) fn create_static_node(&mut self, node: &'b TemplateNode<'static>) {
|
||||
match *node {
|
||||
// Todo: create the children's template
|
||||
TemplateNode::Dynamic(_) => self
|
||||
.mutations
|
||||
.template_mutations
|
||||
.template_edits
|
||||
.push(CreateStaticPlaceholder {}),
|
||||
TemplateNode::Text(value) => self
|
||||
.mutations
|
||||
.template_mutations
|
||||
.template_edits
|
||||
.push(CreateStaticText { value }),
|
||||
TemplateNode::DynamicText { .. } => self
|
||||
.mutations
|
||||
.template_mutations
|
||||
.push(CreateTextPlaceholder),
|
||||
|
||||
TemplateNode::DynamicText { .. } => {
|
||||
self.mutations.template_edits.push(CreateTextPlaceholder)
|
||||
}
|
||||
|
||||
TemplateNode::Element {
|
||||
attrs,
|
||||
@@ -257,21 +259,19 @@ impl<'b> VirtualDom {
|
||||
tag,
|
||||
..
|
||||
} => {
|
||||
if let Some(namespace) = namespace {
|
||||
self.mutations
|
||||
.template_mutations
|
||||
.push(CreateElementNamespace {
|
||||
name: tag,
|
||||
namespace,
|
||||
});
|
||||
} else {
|
||||
self.mutations
|
||||
.template_mutations
|
||||
.push(CreateElement { name: tag });
|
||||
match namespace {
|
||||
Some(namespace) => self.mutations.template_edits.push(CreateElementNamespace {
|
||||
name: tag,
|
||||
namespace,
|
||||
}),
|
||||
None => self
|
||||
.mutations
|
||||
.template_edits
|
||||
.push(CreateElement { name: tag }),
|
||||
}
|
||||
|
||||
self.mutations
|
||||
.template_mutations
|
||||
.template_edits
|
||||
.extend(attrs.iter().filter_map(|attr| match attr {
|
||||
TemplateAttribute::Static {
|
||||
name,
|
||||
@@ -295,7 +295,7 @@ impl<'b> VirtualDom {
|
||||
.for_each(|child| self.create_static_node(child));
|
||||
|
||||
self.mutations
|
||||
.template_mutations
|
||||
.template_edits
|
||||
.push(AppendChildren { m: children.len() })
|
||||
}
|
||||
}
|
||||
@@ -404,7 +404,7 @@ impl<'b> VirtualDom {
|
||||
) -> usize {
|
||||
// Keep track of how many mutations are in the buffer in case we need to split them out if a suspense boundary
|
||||
// is encountered
|
||||
let mutations_to_this_point = self.mutations.len();
|
||||
let mutations_to_this_point = self.mutations.dom_edits.len();
|
||||
|
||||
// Create the component's root element
|
||||
let created = self.create_scope(scope, new);
|
||||
@@ -430,10 +430,10 @@ impl<'b> VirtualDom {
|
||||
// Note that we break off dynamic mutations only - since static mutations aren't rendered immediately
|
||||
let split_off = unsafe {
|
||||
std::mem::transmute::<Vec<Mutation>, Vec<Mutation>>(
|
||||
self.mutations.split_off(mutations_to_this_point),
|
||||
self.mutations.dom_edits.split_off(mutations_to_this_point),
|
||||
)
|
||||
};
|
||||
boundary.mutations.borrow_mut().edits.extend(split_off);
|
||||
boundary.mutations.borrow_mut().dom_edits.extend(split_off);
|
||||
boundary.created_on_stack.set(created);
|
||||
boundary
|
||||
.waiting_on
|
||||
|
||||
+85
-36
@@ -1,57 +1,110 @@
|
||||
use bumpalo::boxed::Box as BumpBox;
|
||||
use std::{
|
||||
any::Any,
|
||||
cell::{Cell, RefCell},
|
||||
fmt::Debug,
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
pub struct UiEvent<T: 'static + ?Sized> {
|
||||
pub(crate) bubbles: Rc<Cell<bool>>,
|
||||
/// A wrapper around some generic data that handles the event's state
|
||||
///
|
||||
///
|
||||
/// Prevent this event from continuing to bubble up the tree to parent elements.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust, ignore
|
||||
/// rsx! {
|
||||
/// button {
|
||||
/// onclick: move |evt: Event<MouseData>| {
|
||||
/// evt.cancel_bubble();
|
||||
///
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub struct Event<T: 'static + ?Sized> {
|
||||
pub(crate) data: Rc<T>,
|
||||
pub(crate) propogates: Rc<Cell<bool>>,
|
||||
}
|
||||
|
||||
impl UiEvent<dyn Any> {
|
||||
pub fn downcast<T: 'static + Sized>(self) -> Option<UiEvent<T>> {
|
||||
Some(UiEvent {
|
||||
bubbles: self.bubbles,
|
||||
data: self.data.downcast().ok()?,
|
||||
})
|
||||
impl<T> Event<T> {
|
||||
/// Prevent this event from continuing to bubble up the tree to parent elements.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust, ignore
|
||||
/// rsx! {
|
||||
/// button {
|
||||
/// onclick: move |evt: Event<MouseData>| {
|
||||
/// evt.cancel_bubble();
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[deprecated = "use stop_propogation instead"]
|
||||
pub fn cancel_bubble(&self) {
|
||||
self.propogates.set(false);
|
||||
}
|
||||
|
||||
/// Prevent this event from continuing to bubble up the tree to parent elements.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust, ignore
|
||||
/// rsx! {
|
||||
/// button {
|
||||
/// onclick: move |evt: Event<MouseData>| {
|
||||
/// evt.cancel_bubble();
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn stop_propogation(&self) {
|
||||
self.propogates.set(false);
|
||||
}
|
||||
|
||||
/// Get a reference to the inner data from this event
|
||||
///
|
||||
/// ```rust, ignore
|
||||
/// rsx! {
|
||||
/// button {
|
||||
/// onclick: move |evt: Event<MouseData>| {
|
||||
/// let data = evt.inner.clone();
|
||||
/// cx.spawn(async move {
|
||||
/// println!("{:?}", data);
|
||||
/// });
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn inner(&self) -> &Rc<T> {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
impl<T: ?Sized> Clone for UiEvent<T> {
|
||||
|
||||
impl<T: ?Sized> Clone for Event<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
bubbles: self.bubbles.clone(),
|
||||
propogates: self.propogates.clone(),
|
||||
data: self.data.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T> UiEvent<T> {
|
||||
pub fn cancel_bubble(&self) {
|
||||
self.bubbles.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::ops::Deref for UiEvent<T> {
|
||||
impl<T> std::ops::Deref for Event<T> {
|
||||
type Target = Rc<T>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Debug> std::fmt::Debug for UiEvent<T> {
|
||||
impl<T: std::fmt::Debug> std::fmt::Debug for Event<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("UiEvent")
|
||||
.field("bubble_state", &self.bubbles)
|
||||
.field("bubble_state", &self.propogates)
|
||||
.field("data", &self.data)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
type ExternalListenerCallback<'bump, T> = BumpBox<'bump, dyn FnMut(T) + 'bump>;
|
||||
|
||||
/// The callback type generated by the `rsx!` macro when an `on` field is specified for components.
|
||||
///
|
||||
/// This makes it possible to pass `move |evt| {}` style closures into components as property fields.
|
||||
@@ -60,9 +113,8 @@ type ExternalListenerCallback<'bump, T> = BumpBox<'bump, dyn FnMut(T) + 'bump>;
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust, ignore
|
||||
///
|
||||
/// rsx!{
|
||||
/// MyComponent { onclick: move |evt| log::info!("clicked"), }
|
||||
/// MyComponent { onclick: move |evt| log::info!("clicked") }
|
||||
/// }
|
||||
///
|
||||
/// #[derive(Props)]
|
||||
@@ -79,22 +131,17 @@ type ExternalListenerCallback<'bump, T> = BumpBox<'bump, dyn FnMut(T) + 'bump>;
|
||||
/// }
|
||||
///
|
||||
/// ```
|
||||
#[derive(Default)]
|
||||
pub struct EventHandler<'bump, T = ()> {
|
||||
/// The (optional) callback that the user specified
|
||||
/// Uses a `RefCell` to allow for interior mutability, and FnMut closures.
|
||||
pub callback: RefCell<Option<ExternalListenerCallback<'bump, T>>>,
|
||||
pub(super) callback: RefCell<Option<ExternalListenerCallback<'bump, T>>>,
|
||||
}
|
||||
|
||||
impl<'a, T> Default for EventHandler<'a, T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
callback: RefCell::new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
type ExternalListenerCallback<'bump, T> = bumpalo::boxed::Box<'bump, dyn FnMut(T) + 'bump>;
|
||||
|
||||
impl<T> EventHandler<'_, T> {
|
||||
/// Call this event handler with the appropriate event type
|
||||
///
|
||||
/// This borrows the event using a RefCell. Recursively calling a listener will cause a panic.
|
||||
pub fn call(&self, event: T) {
|
||||
if let Some(callback) = self.callback.borrow_mut().as_mut() {
|
||||
callback(event);
|
||||
@@ -102,6 +149,8 @@ impl<T> EventHandler<'_, T> {
|
||||
}
|
||||
|
||||
/// Forcibly drop the internal handler callback, releasing memory
|
||||
///
|
||||
/// This will force any future calls to "call" to not doing anything
|
||||
pub fn release(&self) {
|
||||
self.callback.replace(None);
|
||||
}
|
||||
|
||||
+10
-106
@@ -1,119 +1,21 @@
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
fmt::Arguments,
|
||||
};
|
||||
|
||||
use crate::{innerlude::DynamicNode, AttributeValue, Element, LazyNodes, ScopeState, VNode};
|
||||
use bumpalo::boxed::Box as BumpBox;
|
||||
use bumpalo::Bump;
|
||||
use std::fmt::Arguments;
|
||||
use std::future::Future;
|
||||
|
||||
use crate::{
|
||||
any_props::{AnyProps, VProps},
|
||||
arena::ElementId,
|
||||
innerlude::{DynamicNode, EventHandler, VComponent, VText},
|
||||
Attribute, AttributeValue, Element, LazyNodes, Properties, Scope, ScopeState, VNode,
|
||||
};
|
||||
|
||||
impl ScopeState {
|
||||
/// Create some text that's allocated along with the other vnodes
|
||||
pub fn text<'a>(&'a self, args: Arguments) -> DynamicNode<'a> {
|
||||
let (text, _) = self.raw_text(args);
|
||||
DynamicNode::Text(VText {
|
||||
id: Cell::new(ElementId(0)),
|
||||
value: text,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn raw_text_inline<'a>(&'a self, args: Arguments) -> &'a str {
|
||||
self.raw_text(args).0
|
||||
}
|
||||
|
||||
pub fn raw_text<'a>(&'a self, args: Arguments) -> (&'a str, bool) {
|
||||
match args.as_str() {
|
||||
Some(static_str) => (static_str, true),
|
||||
None => {
|
||||
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(), false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fragment_from_iter<'a, 'c, I>(
|
||||
&'a self,
|
||||
node_iter: impl IntoDynNode<'a, I> + 'c,
|
||||
) -> DynamicNode {
|
||||
node_iter.into_vnode(self)
|
||||
}
|
||||
|
||||
/// Create a new [`Attribute`]
|
||||
pub fn attr<'a>(
|
||||
&'a self,
|
||||
name: &'static str,
|
||||
value: impl IntoAttributeValue<'a>,
|
||||
namespace: Option<&'static str>,
|
||||
volatile: bool,
|
||||
) -> Attribute<'a> {
|
||||
Attribute {
|
||||
name,
|
||||
namespace,
|
||||
volatile,
|
||||
value: value.into_value(self.bump()),
|
||||
mounted_element: Cell::new(ElementId(0)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new [`VNode::Component`]
|
||||
pub fn component<'a, P, A, F: ComponentReturn<'a, A>>(
|
||||
&'a self,
|
||||
component: fn(Scope<'a, P>) -> F,
|
||||
props: P,
|
||||
fn_name: &'static str,
|
||||
) -> DynamicNode<'a>
|
||||
where
|
||||
P: Properties + 'a,
|
||||
{
|
||||
let as_component = component;
|
||||
let vcomp = VProps::new(as_component, P::memoize, props);
|
||||
let as_dyn: Box<dyn AnyProps<'a>> = Box::new(vcomp);
|
||||
let extended: Box<dyn AnyProps> = unsafe { std::mem::transmute(as_dyn) };
|
||||
|
||||
// let as_dyn: &dyn AnyProps = self.bump().alloc(vcomp);
|
||||
// todo: clean up borrowed props
|
||||
// if !P::IS_STATIC {
|
||||
// let vcomp = ex;
|
||||
// let vcomp = unsafe { std::mem::transmute(vcomp) };
|
||||
// self.items.borrow_mut().borrowed_props.push(vcomp);
|
||||
// }
|
||||
|
||||
DynamicNode::Component(VComponent {
|
||||
name: fn_name,
|
||||
render_fn: component as *const (),
|
||||
static_props: P::IS_STATIC,
|
||||
props: Cell::new(Some(extended)),
|
||||
scope: Cell::new(None),
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a new [`EventHandler`] from an [`FnMut`]
|
||||
pub fn event_handler<'a, T>(&'a self, f: impl FnMut(T) + 'a) -> EventHandler<'a, 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));
|
||||
EventHandler { callback }
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait ComponentReturn<'a, A = ()> {
|
||||
fn into_return(self, cx: &'a ScopeState) -> RenderReturn<'a>;
|
||||
}
|
||||
|
||||
impl<'a> ComponentReturn<'a> for Element<'a> {
|
||||
fn into_return(self, _cx: &ScopeState) -> RenderReturn<'a> {
|
||||
RenderReturn::Sync(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct AsyncMarker;
|
||||
impl<'a, F> ComponentReturn<'a, AsyncMarker> for F
|
||||
where
|
||||
@@ -142,6 +44,7 @@ impl<'a> RenderReturn<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait IntoDynNode<'a, A = ()> {
|
||||
fn into_vnode(self, cx: &'a ScopeState) -> DynamicNode<'a>;
|
||||
}
|
||||
@@ -191,19 +94,19 @@ impl<'a, 'b> IntoDynNode<'a> for LazyNodes<'a, 'b> {
|
||||
|
||||
impl<'a> IntoDynNode<'_> for &'a str {
|
||||
fn into_vnode(self, cx: &ScopeState) -> DynamicNode {
|
||||
cx.text(format_args!("{}", self))
|
||||
cx.text_node(format_args!("{}", self))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoDynNode<'_> for String {
|
||||
fn into_vnode(self, cx: &ScopeState) -> DynamicNode {
|
||||
cx.text(format_args!("{}", self))
|
||||
cx.text_node(format_args!("{}", self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> IntoDynNode<'b> for Arguments<'_> {
|
||||
fn into_vnode(self, cx: &'b ScopeState) -> DynamicNode<'b> {
|
||||
cx.text(self)
|
||||
cx.text_node(self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,6 +138,7 @@ impl<'a, 'b> IntoTemplate<'a> for LazyNodes<'a, 'b> {
|
||||
}
|
||||
|
||||
// Note that we're using the E as a generic but this is never crafted anyways.
|
||||
#[doc(hidden)]
|
||||
pub struct FromNodeIterator;
|
||||
impl<'a, T, I> IntoDynNode<'a, FromNodeIterator> for T
|
||||
where
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//! 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 [`NodeFactory`] closures.
|
||||
//! 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.
|
||||
@@ -27,13 +27,11 @@ pub struct LazyNodes<'a, 'b> {
|
||||
inner: StackNodeStorage<'a, 'b>,
|
||||
}
|
||||
|
||||
pub type NodeFactory<'a> = &'a ScopeState;
|
||||
|
||||
type StackHeapSize = [usize; 16];
|
||||
|
||||
enum StackNodeStorage<'a, 'b> {
|
||||
Stack(LazyStack),
|
||||
Heap(Box<dyn FnMut(Option<NodeFactory<'a>>) -> Option<VNode<'a>> + 'b>),
|
||||
Heap(Box<dyn FnMut(Option<&'a ScopeState>) -> Option<VNode<'a>> + 'b>),
|
||||
}
|
||||
|
||||
impl<'a, 'b> LazyNodes<'a, 'b> {
|
||||
@@ -42,11 +40,11 @@ impl<'a, 'b> LazyNodes<'a, 'b> {
|
||||
/// 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(NodeFactory<'a>) -> VNode<'a> + 'b) -> Self {
|
||||
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);
|
||||
|
||||
let val = move |fac: Option<NodeFactory<'a>>| {
|
||||
let val = move |fac: Option<&'a ScopeState>| {
|
||||
fac.map(
|
||||
slot.take()
|
||||
.expect("LazyNodes closure to be called only once"),
|
||||
@@ -67,13 +65,13 @@ impl<'a, 'b> LazyNodes<'a, 'b> {
|
||||
/// Create a new [`LazyNodes`] closure, but force it onto the heap.
|
||||
pub fn new_boxed<F>(inner: F) -> Self
|
||||
where
|
||||
F: FnOnce(NodeFactory<'a>) -> VNode<'a> + 'b,
|
||||
F: FnOnce(&'a ScopeState) -> VNode<'a> + 'b,
|
||||
{
|
||||
// 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(inner);
|
||||
|
||||
Self {
|
||||
inner: StackNodeStorage::Heap(Box::new(move |fac: Option<NodeFactory<'a>>| {
|
||||
inner: StackNodeStorage::Heap(Box::new(move |fac: Option<&'a ScopeState>| {
|
||||
fac.map(
|
||||
slot.take()
|
||||
.expect("LazyNodes closure to be called only once"),
|
||||
@@ -84,9 +82,9 @@ impl<'a, 'b> LazyNodes<'a, 'b> {
|
||||
|
||||
unsafe fn new_inner<F>(val: F) -> Self
|
||||
where
|
||||
F: FnMut(Option<NodeFactory<'a>>) -> Option<VNode<'a>> + 'b,
|
||||
F: FnMut(Option<&'a ScopeState>) -> Option<VNode<'a>> + 'b,
|
||||
{
|
||||
let mut ptr: *const _ = &val as &dyn FnMut(Option<NodeFactory<'a>>) -> Option<VNode<'a>>;
|
||||
let mut ptr: *const _ = &val as &dyn FnMut(Option<&'a ScopeState>) -> Option<VNode<'a>>;
|
||||
|
||||
assert_eq!(
|
||||
ptr as *const u8, &val as *const _ as *const u8,
|
||||
@@ -162,12 +160,10 @@ impl<'a, 'b> LazyNodes<'a, 'b> {
|
||||
/// ```rust, ignore
|
||||
/// let f = LazyNodes::new(move |f| f.element("div", [], [], [] None));
|
||||
///
|
||||
/// let fac = NodeFactory::new(&cx);
|
||||
///
|
||||
/// let node = f.call(cac);
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn call(self, f: NodeFactory<'a>) -> VNode<'a> {
|
||||
pub fn call(self, f: &'a ScopeState) -> VNode<'a> {
|
||||
match self.inner {
|
||||
StackNodeStorage::Heap(mut lazy) => {
|
||||
lazy(Some(f)).expect("Closure should not be called twice")
|
||||
@@ -184,18 +180,18 @@ struct LazyStack {
|
||||
}
|
||||
|
||||
impl LazyStack {
|
||||
fn call<'a>(&mut self, f: NodeFactory<'a>) -> VNode<'a> {
|
||||
fn call<'a>(&mut self, f: &'a ScopeState) -> VNode<'a> {
|
||||
let LazyStack { buf, .. } = self;
|
||||
let data = buf.as_ref();
|
||||
|
||||
let info_size =
|
||||
mem::size_of::<*mut dyn FnMut(Option<NodeFactory<'a>>) -> Option<VNode<'a>>>()
|
||||
mem::size_of::<*mut dyn FnMut(Option<&'a ScopeState>) -> Option<VNode<'a>>>()
|
||||
/ mem::size_of::<usize>()
|
||||
- 1;
|
||||
|
||||
let info_ofs = data.len() - info_size;
|
||||
|
||||
let g: *mut dyn FnMut(Option<NodeFactory<'a>>) -> Option<VNode<'a>> =
|
||||
let g: *mut dyn FnMut(Option<&'a ScopeState>) -> Option<VNode<'a>> =
|
||||
unsafe { make_fat_ptr(data[..].as_ptr() as usize, &data[info_ofs..]) };
|
||||
|
||||
self.dropped = true;
|
||||
@@ -210,14 +206,14 @@ impl Drop for LazyStack {
|
||||
let LazyStack { buf, .. } = self;
|
||||
let data = buf.as_ref();
|
||||
|
||||
let info_size = mem::size_of::<
|
||||
*mut dyn FnMut(Option<NodeFactory<'_>>) -> Option<VNode<'_>>,
|
||||
>() / mem::size_of::<usize>()
|
||||
- 1;
|
||||
let info_size =
|
||||
mem::size_of::<*mut dyn FnMut(Option<&ScopeState>) -> Option<VNode<'_>>>()
|
||||
/ mem::size_of::<usize>()
|
||||
- 1;
|
||||
|
||||
let info_ofs = data.len() - info_size;
|
||||
|
||||
let g: *mut dyn FnMut(Option<NodeFactory<'_>>) -> Option<VNode<'_>> =
|
||||
let g: *mut dyn FnMut(Option<&ScopeState>) -> Option<VNode<'_>> =
|
||||
unsafe { make_fat_ptr(data[..].as_ptr() as usize, &data[info_ofs..]) };
|
||||
|
||||
self.dropped = true;
|
||||
@@ -252,73 +248,3 @@ unsafe fn make_fat_ptr<T: ?Sized>(data_ptr: usize, meta_vals: &[usize]) -> *mut
|
||||
fn round_to_words(len: usize) -> usize {
|
||||
(len + mem::size_of::<usize>() - 1) / mem::size_of::<usize>()
|
||||
}
|
||||
|
||||
// #[cfg(test)]
|
||||
// mod tests {
|
||||
// use super::*;
|
||||
// use crate::innerlude::{Element, Scope, VirtualDom};
|
||||
|
||||
// #[test]
|
||||
// fn it_works() {
|
||||
// fn app(cx: Scope<()>) -> Element {
|
||||
// cx.render(LazyNodes::new(|f| f.text(format_args!("hello world!"))))
|
||||
// }
|
||||
|
||||
// let mut dom = VirtualDom::new(app);
|
||||
// dom.rebuild();
|
||||
|
||||
// let g = dom.base_scope().root_node();
|
||||
// dbg!(g);
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn it_drops() {
|
||||
// use std::rc::Rc;
|
||||
|
||||
// struct AppProps {
|
||||
// inner: Rc<i32>,
|
||||
// }
|
||||
|
||||
// fn app(cx: Scope<AppProps>) -> Element {
|
||||
// struct DropInner {
|
||||
// id: i32,
|
||||
// }
|
||||
// impl Drop for DropInner {
|
||||
// fn drop(&mut self) {
|
||||
// eprintln!("dropping inner");
|
||||
// }
|
||||
// }
|
||||
|
||||
// let caller = {
|
||||
// let it = (0..10).map(|i| {
|
||||
// let val = cx.props.inner.clone();
|
||||
// LazyNodes::new(move |f| {
|
||||
// eprintln!("hell closure");
|
||||
// let inner = DropInner { id: i };
|
||||
// f.text(format_args!("hello world {:?}, {:?}", inner.id, val))
|
||||
// })
|
||||
// });
|
||||
|
||||
// LazyNodes::new(|f| {
|
||||
// eprintln!("main closure");
|
||||
// f.fragment_from_iter(it)
|
||||
// })
|
||||
// };
|
||||
|
||||
// cx.render(caller)
|
||||
// }
|
||||
|
||||
// let inner = Rc::new(0);
|
||||
// let mut dom = VirtualDom::new_with_props(
|
||||
// app,
|
||||
// AppProps {
|
||||
// inner: inner.clone(),
|
||||
// },
|
||||
// );
|
||||
// dom.rebuild();
|
||||
|
||||
// drop(dom);
|
||||
|
||||
// assert_eq!(Rc::strong_count(&inner), 1);
|
||||
// }
|
||||
// }
|
||||
@@ -1,4 +1,5 @@
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
mod any_props;
|
||||
mod arena;
|
||||
@@ -67,43 +68,13 @@ pub(crate) mod innerlude {
|
||||
/// )
|
||||
/// ```
|
||||
pub type Component<P = ()> = fn(Scope<P>) -> Element;
|
||||
|
||||
/// A list of attributes
|
||||
pub type Attributes<'a> = Option<&'a [Attribute<'a>]>;
|
||||
}
|
||||
|
||||
pub use crate::innerlude::{
|
||||
// AnyAttributeValue, AnyEvent,
|
||||
fc_to_builder,
|
||||
Attribute,
|
||||
AttributeValue,
|
||||
Attributes,
|
||||
Component,
|
||||
DynamicNode,
|
||||
Element,
|
||||
ElementId,
|
||||
Fragment,
|
||||
LazyNodes,
|
||||
Mutation,
|
||||
Mutations,
|
||||
NodeFactory,
|
||||
Properties,
|
||||
RenderReturn,
|
||||
Scope,
|
||||
ScopeId,
|
||||
ScopeState,
|
||||
Scoped,
|
||||
SuspenseBoundary,
|
||||
SuspenseContext,
|
||||
TaskId,
|
||||
Template,
|
||||
TemplateAttribute,
|
||||
TemplateNode,
|
||||
UiEvent,
|
||||
VComponent,
|
||||
VNode,
|
||||
VText,
|
||||
VirtualDom,
|
||||
fc_to_builder, Attribute, AttributeValue, Component, DynamicNode, Element, ElementId, Event,
|
||||
Fragment, LazyNodes, Mutation, Mutations, Properties, RenderReturn, Scope, ScopeId, ScopeState,
|
||||
Scoped, SuspenseBoundary, SuspenseContext, TaskId, Template, TemplateAttribute, TemplateNode,
|
||||
VComponent, VNode, VText, VirtualDom,
|
||||
};
|
||||
|
||||
/// The purpose of this module is to alleviate imports of many common types
|
||||
@@ -111,9 +82,9 @@ pub use crate::innerlude::{
|
||||
/// This includes types like [`Scope`], [`Element`], and [`Component`].
|
||||
pub mod prelude {
|
||||
pub use crate::innerlude::{
|
||||
fc_to_builder, Element, EventHandler, Fragment, LazyNodes, NodeFactory, Properties, Scope,
|
||||
ScopeId, ScopeState, Scoped, TaskId, Template, TemplateAttribute, TemplateNode, UiEvent,
|
||||
VNode, VirtualDom,
|
||||
fc_to_builder, Element, Event, EventHandler, Fragment, LazyNodes, Properties, Scope,
|
||||
ScopeId, ScopeState, Scoped, TaskId, Template, TemplateAttribute, TemplateNode, VNode,
|
||||
VirtualDom,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -121,28 +92,4 @@ 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;
|
||||
pub use futures_channel;
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
/// A helper macro for using hooks in async environements.
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
///
|
||||
/// ```ignore
|
||||
/// let (data) = use_ref(&cx, || {});
|
||||
///
|
||||
/// let handle_thing = move |_| {
|
||||
/// to_owned![data]
|
||||
/// cx.spawn(async move {
|
||||
/// // do stuff
|
||||
/// });
|
||||
/// }
|
||||
/// ```
|
||||
macro_rules! to_owned {
|
||||
($($es:ident),+) => {$(
|
||||
#[allow(unused_mut)]
|
||||
let mut $es = $es.to_owned();
|
||||
)*}
|
||||
}
|
||||
@@ -1,45 +1,50 @@
|
||||
use fxhash::FxHashSet;
|
||||
|
||||
use crate::{arena::ElementId, ScopeId};
|
||||
|
||||
/// A container for all the relevant steps to modify the Real DOM
|
||||
///
|
||||
/// This method returns "mutations" - IE the necessary changes to get the RealDOM to match the VirtualDOM. It also
|
||||
/// includes a list of NodeRefs that need to be applied and effects that need to be triggered after the RealDOM has
|
||||
/// applied the edits.
|
||||
///
|
||||
/// Mutations are the only link between the RealDOM and the VirtualDOM.
|
||||
#[derive(Debug, Default)]
|
||||
#[must_use = "not handling edits can lead to visual inconsistencies in UI"]
|
||||
pub struct Mutations<'a> {
|
||||
/// The ID of the subtree that these edits are targetting
|
||||
pub subtree: usize,
|
||||
pub template_mutations: Vec<Mutation<'a>>,
|
||||
pub edits: Vec<Mutation<'a>>,
|
||||
|
||||
/// The list of Scopes that were diffed, created, and removed during the Diff process.
|
||||
pub dirty_scopes: FxHashSet<ScopeId>,
|
||||
|
||||
/// Any mutations required to build the templates using [`Mutations`]
|
||||
pub template_edits: Vec<Mutation<'a>>,
|
||||
|
||||
/// Any mutations required to patch the renderer to match the layout of the VirtualDom
|
||||
pub dom_edits: Vec<Mutation<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Mutations<'a> {
|
||||
/// A useful tool for testing mutations
|
||||
///
|
||||
/// Rewrites IDs to just be "template", so you can compare the mutations
|
||||
///
|
||||
/// Used really only for testing
|
||||
pub fn santize(mut self) -> Self {
|
||||
for edit in self
|
||||
.template_mutations
|
||||
self.template_edits
|
||||
.iter_mut()
|
||||
.chain(self.edits.iter_mut())
|
||||
{
|
||||
match edit {
|
||||
.chain(self.dom_edits.iter_mut())
|
||||
.for_each(|edit| match edit {
|
||||
Mutation::LoadTemplate { name, .. } => *name = "template",
|
||||
Mutation::SaveTemplate { name, .. } => *name = "template",
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> std::ops::Deref for Mutations<'a> {
|
||||
type Target = Vec<Mutation<'a>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.edits
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for Mutations<'_> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.edits
|
||||
/// Push a new mutation into the dom_edits list
|
||||
pub(crate) fn push(&mut self, mutation: Mutation<'static>) {
|
||||
self.dom_edits.push(mutation)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +59,11 @@ each subtree has its own numbering scheme
|
||||
)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Mutation<'a> {
|
||||
/// Pop the topmost node from our stack and append them to the node
|
||||
/// at the top of the stack.
|
||||
AppendChildren {
|
||||
/// How many nodes should be popped from the stack.
|
||||
/// The node remaining on the stack will be the target for the append.
|
||||
m: usize,
|
||||
},
|
||||
|
||||
@@ -122,36 +131,61 @@ pub enum Mutation<'a> {
|
||||
m: usize,
|
||||
},
|
||||
|
||||
/// Save the top m nodes as a placeholder
|
||||
SaveTemplate {
|
||||
/// The name of the template that we're saving
|
||||
name: &'static str,
|
||||
|
||||
/// How many nodes are being saved into this template
|
||||
m: usize,
|
||||
},
|
||||
|
||||
/// Set the value of a node's attribute.
|
||||
SetAttribute {
|
||||
/// The name of the attribute to set.
|
||||
name: &'a str,
|
||||
/// The value of the attribute.
|
||||
value: &'a str,
|
||||
|
||||
/// The ID of the node to set the attribute of.
|
||||
id: ElementId,
|
||||
|
||||
// value: &'bump str,
|
||||
/// The (optional) namespace of the attribute.
|
||||
/// For instance, "style" is in the "style" namespace.
|
||||
ns: Option<&'a str>,
|
||||
},
|
||||
|
||||
/// Set the value of a node's attribute.
|
||||
SetStaticAttribute {
|
||||
/// The name of the attribute to set.
|
||||
name: &'a str,
|
||||
|
||||
/// The value of the attribute.
|
||||
value: &'a str,
|
||||
|
||||
/// The (optional) namespace of the attribute.
|
||||
/// For instance, "style" is in the "style" namespace.
|
||||
ns: Option<&'a str>,
|
||||
},
|
||||
|
||||
/// Set the value of a node's attribute.
|
||||
SetBoolAttribute {
|
||||
/// The name of the attribute to set.
|
||||
name: &'a str,
|
||||
|
||||
/// The value of the attribute.
|
||||
value: bool,
|
||||
|
||||
/// The ID of the node to set the attribute of.
|
||||
id: ElementId,
|
||||
},
|
||||
|
||||
/// Set the textcontent of a node.
|
||||
SetText {
|
||||
/// The textcontent of the node
|
||||
value: &'a str,
|
||||
|
||||
/// The ID of the node to set the textcontent of.
|
||||
id: ElementId,
|
||||
},
|
||||
|
||||
@@ -175,11 +209,16 @@ pub enum Mutation<'a> {
|
||||
/// The ID of the node to remove.
|
||||
id: ElementId,
|
||||
},
|
||||
|
||||
/// Remove a particular node from the DOM
|
||||
Remove {
|
||||
/// The ID of the node to remove.
|
||||
id: ElementId,
|
||||
},
|
||||
|
||||
/// Push the given root node onto our stack.
|
||||
PushRoot {
|
||||
/// The ID of the root node to push.
|
||||
id: ElementId,
|
||||
},
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{any_props::AnyProps, arena::ElementId, Element, ScopeId, ScopeState, UiEvent};
|
||||
use crate::{any_props::AnyProps, arena::ElementId, Element, Event, ScopeId, ScopeState};
|
||||
use bumpalo::boxed::Box as BumpBox;
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
@@ -70,7 +70,6 @@ pub struct Template<'a> {
|
||||
pub attr_paths: &'a [&'a [u8]],
|
||||
}
|
||||
|
||||
/// A weird-ish variant of VNodes with way more limited types
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum TemplateNode<'a> {
|
||||
Element {
|
||||
@@ -98,7 +97,7 @@ impl<'a> DynamicNode<'a> {
|
||||
matches!(self, DynamicNode::Component(_))
|
||||
}
|
||||
pub fn placeholder() -> Self {
|
||||
Self::Placeholder(Cell::new(ElementId(0)))
|
||||
Self::Placeholder(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,18 +155,18 @@ pub enum AttributeValue<'a> {
|
||||
None,
|
||||
}
|
||||
|
||||
type ListenerCb<'a> = BumpBox<'a, dyn FnMut(UiEvent<dyn Any>) + 'a>;
|
||||
type ListenerCb<'a> = BumpBox<'a, dyn FnMut(Event<dyn Any>) + 'a>;
|
||||
|
||||
impl<'a> AttributeValue<'a> {
|
||||
pub fn new_listener<T: 'static>(
|
||||
cx: &'a ScopeState,
|
||||
mut callback: impl FnMut(UiEvent<T>) + 'a,
|
||||
mut callback: impl FnMut(Event<T>) + 'a,
|
||||
) -> AttributeValue<'a> {
|
||||
let boxed: BumpBox<'a, dyn FnMut(_) + 'a> = unsafe {
|
||||
BumpBox::from_raw(cx.bump().alloc(move |event: UiEvent<dyn Any>| {
|
||||
BumpBox::from_raw(cx.bump().alloc(move |event: Event<dyn Any>| {
|
||||
if let Ok(data) = event.data.downcast::<T>() {
|
||||
callback(UiEvent {
|
||||
bubbles: event.bubbles,
|
||||
callback(Event {
|
||||
propogates: event.propogates,
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -15,25 +15,11 @@ pub type SuspenseContext = Rc<SuspenseBoundary>;
|
||||
|
||||
/// Essentially a fiber in React
|
||||
pub struct SuspenseBoundary {
|
||||
pub id: ScopeId,
|
||||
pub waiting_on: RefCell<HashSet<SuspenseId>>,
|
||||
pub mutations: RefCell<Mutations<'static>>,
|
||||
pub placeholder: Cell<Option<ElementId>>,
|
||||
|
||||
pub created_on_stack: Cell<usize>,
|
||||
|
||||
// whenever the suspense resolves, we call this onresolve function
|
||||
// this lets us do things like putting up a loading spinner
|
||||
//
|
||||
// todo: we need a way of controlling whether or not a component hides itself but still processes changes
|
||||
// If we run into suspense, we perform a diff, so its important that the old elements are still around.
|
||||
//
|
||||
// When the timer expires, I imagine a container could hide the elements and show the spinner. This, however,
|
||||
// can not be
|
||||
pub onresolve: Option<Box<dyn FnOnce()>>,
|
||||
|
||||
/// Called when
|
||||
pub onstart: Option<Box<dyn FnOnce()>>,
|
||||
pub(crate) id: ScopeId,
|
||||
pub(crate) waiting_on: RefCell<HashSet<SuspenseId>>,
|
||||
pub(crate) mutations: RefCell<Mutations<'static>>,
|
||||
pub(crate) placeholder: Cell<Option<ElementId>>,
|
||||
pub(crate) created_on_stack: Cell<usize>,
|
||||
}
|
||||
|
||||
impl SuspenseBoundary {
|
||||
@@ -44,8 +30,6 @@ impl SuspenseBoundary {
|
||||
mutations: RefCell::new(Mutations::default()),
|
||||
placeholder: Cell::new(None),
|
||||
created_on_stack: Cell::new(0),
|
||||
onresolve: None,
|
||||
onstart: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ impl VirtualDom {
|
||||
.and_then(|id| self.scopes.get_mut(id.0).map(|f| f.as_mut() as *mut _))
|
||||
}
|
||||
|
||||
pub fn ensure_drop_safety(&self, scope: ScopeId) {
|
||||
fn ensure_drop_safety(&self, scope: ScopeId) {
|
||||
let scope = &self.scopes[scope.0];
|
||||
let node = unsafe { scope.previous_frame().try_load_node() };
|
||||
|
||||
|
||||
+102
-8
@@ -1,18 +1,21 @@
|
||||
use crate::{
|
||||
any_props::AnyProps,
|
||||
any_props::VProps,
|
||||
arena::ElementId,
|
||||
bump_frame::BumpFrame,
|
||||
factory::RenderReturn,
|
||||
factory::{ComponentReturn, IntoAttributeValue, IntoDynNode, RenderReturn},
|
||||
innerlude::{DynamicNode, EventHandler, VComponent, VText},
|
||||
innerlude::{Scheduler, SchedulerMsg},
|
||||
lazynodes::LazyNodes,
|
||||
Element, TaskId,
|
||||
Attribute, Element, Properties, TaskId,
|
||||
};
|
||||
use bumpalo::Bump;
|
||||
use std::future::Future;
|
||||
use bumpalo::{boxed::Box as BumpBox, Bump};
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
cell::{Cell, RefCell},
|
||||
collections::{HashMap, HashSet},
|
||||
fmt::Arguments,
|
||||
future::Future,
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
};
|
||||
@@ -59,9 +62,9 @@ impl<'a, T> std::ops::Deref for Scoped<'a, T> {
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
|
||||
pub struct ScopeId(pub usize);
|
||||
|
||||
/// A component's state.
|
||||
/// A component's state separate from its props.
|
||||
///
|
||||
/// This struct stores all the important information about a component's state without the props.
|
||||
/// This struct exists to provide a common interface for all scopes without relying on generics.
|
||||
pub struct ScopeState {
|
||||
pub(crate) render_cnt: Cell<usize>,
|
||||
|
||||
@@ -86,7 +89,7 @@ pub struct ScopeState {
|
||||
pub(crate) placeholder: Cell<Option<ElementId>>,
|
||||
}
|
||||
|
||||
impl ScopeState {
|
||||
impl<'src> ScopeState {
|
||||
pub(crate) fn current_frame(&self) -> &BumpFrame {
|
||||
match self.render_cnt.get() % 2 {
|
||||
0 => &self.node_arena_1,
|
||||
@@ -359,10 +362,101 @@ impl ScopeState {
|
||||
/// cx.render(lazy_tree)
|
||||
/// }
|
||||
///```
|
||||
pub fn render<'src>(&'src self, rsx: LazyNodes<'src, '_>) -> Element<'src> {
|
||||
pub fn render(&'src self, rsx: LazyNodes<'src, '_>) -> Element<'src> {
|
||||
Ok(rsx.call(self))
|
||||
}
|
||||
|
||||
/// Create a dynamic text node using [`Arguments`] and the [`ScopeState`]'s internal [`Bump`] allocator
|
||||
pub fn text_node(&'src self, args: Arguments) -> DynamicNode<'src> {
|
||||
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_vnode(self)
|
||||
}
|
||||
|
||||
/// Create a new [`Attribute`] from a name, value, namespace, and volatile bool
|
||||
///
|
||||
/// "Volatile" referes to whether or not Dioxus should always override the value. This helps prevent the UI in
|
||||
/// some renderers stay in sync with the VirtualDom's understanding of the world
|
||||
pub fn attr(
|
||||
&'src self,
|
||||
name: &'static str,
|
||||
value: impl IntoAttributeValue<'src>,
|
||||
namespace: Option<&'static str>,
|
||||
volatile: bool,
|
||||
) -> Attribute<'src> {
|
||||
Attribute {
|
||||
name,
|
||||
namespace,
|
||||
volatile,
|
||||
mounted_element: Default::default(),
|
||||
value: value.into_value(self.bump()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new [`DynamicNode::Component`] variant
|
||||
///
|
||||
///
|
||||
/// The given component can be any of four signatures. Remember that an [`Element`] is really a [`Result<VNode>`].
|
||||
///
|
||||
/// ```rust, ignore
|
||||
/// // Without explicit props
|
||||
/// fn(Scope) -> Element;
|
||||
/// async fn(Scope<'_>) -> Element;
|
||||
///
|
||||
/// // With explicit props
|
||||
/// fn(Scope<Props>) -> Element;
|
||||
/// async fn(Scope<Props<'_>>) -> Element;
|
||||
/// ```
|
||||
pub fn component<P, A, F: ComponentReturn<'src, A>>(
|
||||
&'src self,
|
||||
component: fn(Scope<'src, P>) -> F,
|
||||
props: P,
|
||||
fn_name: &'static str,
|
||||
) -> DynamicNode<'src>
|
||||
where
|
||||
P: Properties + 'src,
|
||||
{
|
||||
let vcomp = VProps::new(component, P::memoize, props);
|
||||
|
||||
// cast off the lifetime of the render return
|
||||
let as_dyn: Box<dyn AnyProps<'src> + '_> = Box::new(vcomp);
|
||||
let extended: Box<dyn AnyProps<'src> + 'src> = unsafe { std::mem::transmute(as_dyn) };
|
||||
|
||||
DynamicNode::Component(VComponent {
|
||||
name: fn_name,
|
||||
render_fn: component as *const (),
|
||||
static_props: P::IS_STATIC,
|
||||
props: Cell::new(Some(extended)),
|
||||
scope: Cell::new(None),
|
||||
})
|
||||
}
|
||||
|
||||
/// 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));
|
||||
EventHandler { callback }
|
||||
}
|
||||
|
||||
/// Store a value between renders. The foundational hook for all other hooks.
|
||||
///
|
||||
/// Accepts an `initializer` closure, which is run on the first use of the hook (typically the initial render). The return value of this closure is stored for the lifetime of the component, and a mutable reference to it is provided on every render as the return value of `use_hook`.
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::{
|
||||
nodes::{Template, TemplateId},
|
||||
scheduler::{SuspenseBoundary, SuspenseId},
|
||||
scopes::{ScopeId, ScopeState},
|
||||
AttributeValue, Element, Scope, SuspenseContext, UiEvent,
|
||||
AttributeValue, Element, Event, Scope, SuspenseContext,
|
||||
};
|
||||
use futures_util::{pin_mut, StreamExt};
|
||||
use slab::Slab;
|
||||
@@ -350,8 +350,8 @@ impl VirtualDom {
|
||||
let mut listeners = vec![];
|
||||
|
||||
// We will clone this later. The data itself is wrapped in RC to be used in callbacks if required
|
||||
let uievent = UiEvent {
|
||||
bubbles: Rc::new(Cell::new(bubbles)),
|
||||
let uievent = Event {
|
||||
propogates: Rc::new(Cell::new(bubbles)),
|
||||
data,
|
||||
};
|
||||
|
||||
@@ -395,7 +395,7 @@ impl VirtualDom {
|
||||
cb(uievent.clone());
|
||||
}
|
||||
|
||||
if !uievent.bubbles.get() {
|
||||
if !uievent.propogates.get() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -534,9 +534,12 @@ impl VirtualDom {
|
||||
let context = scope.has_context::<SuspenseContext>().unwrap();
|
||||
|
||||
self.mutations
|
||||
.extend(context.mutations.borrow_mut().template_mutations.drain(..));
|
||||
.template_edits
|
||||
.extend(context.mutations.borrow_mut().template_edits.drain(..));
|
||||
|
||||
self.mutations
|
||||
.extend(context.mutations.borrow_mut().drain(..));
|
||||
.dom_edits
|
||||
.extend(context.mutations.borrow_mut().dom_edits.drain(..));
|
||||
|
||||
// TODO: count how many nodes are on the stack?
|
||||
self.mutations.push(Mutation::ReplaceWith {
|
||||
@@ -556,7 +559,7 @@ impl VirtualDom {
|
||||
}
|
||||
|
||||
// Save the current mutations length so we can split them into boundary
|
||||
let mutations_to_this_point = self.mutations.len();
|
||||
let mutations_to_this_point = self.mutations.dom_edits.len();
|
||||
|
||||
// Run the scope and get the mutations
|
||||
self.run_scope(dirty.id);
|
||||
@@ -575,7 +578,8 @@ impl VirtualDom {
|
||||
boundary_mut
|
||||
.mutations
|
||||
.borrow_mut()
|
||||
.extend(self.mutations.split_off(mutations_to_this_point));
|
||||
.dom_edits
|
||||
.extend(self.mutations.dom_edits.split_off(mutations_to_this_point));
|
||||
|
||||
// Attach suspended leaves
|
||||
boundary
|
||||
|
||||
Reference in new issue
Block a user