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
+7
-5
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"packages/dioxus",
|
"packages/dioxus",
|
||||||
@@ -12,17 +11,20 @@ members = [
|
|||||||
"packages/desktop",
|
"packages/desktop",
|
||||||
"packages/mobile",
|
"packages/mobile",
|
||||||
"packages/interpreter",
|
"packages/interpreter",
|
||||||
# "packages/tui",
|
|
||||||
"packages/fermi",
|
"packages/fermi",
|
||||||
"packages/liveview",
|
"packages/liveview",
|
||||||
"packages/autofmt",
|
"packages/autofmt",
|
||||||
"packages/rsx",
|
"packages/rsx",
|
||||||
|
"docs/guide",
|
||||||
|
# "packages/tui",
|
||||||
# "packages/native-core",
|
# "packages/native-core",
|
||||||
# "packages/native-core-macro",
|
# "packages/native-core-macro",
|
||||||
# "packages/edit-stream",
|
|
||||||
"docs/guide",
|
|
||||||
]
|
]
|
||||||
|
exclude = [
|
||||||
|
"packages/tui",
|
||||||
|
"packages/native-core",
|
||||||
|
"packages/native-core-macro",
|
||||||
|
]
|
||||||
|
|
||||||
# This is a "virtual package"
|
# This is a "virtual package"
|
||||||
# It is not meant to be published, but is used so "cargo run --example XYZ" works properly
|
# It is not meant to be published, but is used so "cargo run --example XYZ" works properly
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ impl Buffer {
|
|||||||
match node {
|
match node {
|
||||||
BodyNode::Element(el) => self.write_element(el),
|
BodyNode::Element(el) => self.write_element(el),
|
||||||
BodyNode::Component(component) => self.write_component(component),
|
BodyNode::Component(component) => self.write_component(component),
|
||||||
BodyNode::DynamicText(text) => self.write_text(text),
|
BodyNode::Text(text) => self.write_text(text),
|
||||||
BodyNode::RawExpr(exp) => self.write_raw_expr(exp),
|
BodyNode::RawExpr(exp) => self.write_raw_expr(exp),
|
||||||
_ => Ok(()),
|
_ => Ok(()),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -280,7 +280,7 @@ impl Buffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match children {
|
match children {
|
||||||
[BodyNode::DynamicText(ref text)] => Some(text.source.as_ref().unwrap().value().len()),
|
[BodyNode::Text(ref text)] => Some(text.source.as_ref().unwrap().value().len()),
|
||||||
[BodyNode::Component(ref comp)] => {
|
[BodyNode::Component(ref comp)] => {
|
||||||
let attr_len = self.field_len(&comp.fields, &comp.manual_props);
|
let attr_len = self.field_len(&comp.fields, &comp.manual_props);
|
||||||
|
|
||||||
|
|||||||
+23
-23
@@ -228,27 +228,29 @@ impl<'b> VirtualDom {
|
|||||||
self.create_static_node(node);
|
self.create_static_node(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.mutations.template_mutations.push(SaveTemplate {
|
self.mutations.template_edits.push(SaveTemplate {
|
||||||
name: template.template.id,
|
name: template.template.id,
|
||||||
m: template.template.roots.len(),
|
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>) {
|
pub(crate) fn create_static_node(&mut self, node: &'b TemplateNode<'static>) {
|
||||||
match *node {
|
match *node {
|
||||||
// Todo: create the children's template
|
// Todo: create the children's template
|
||||||
TemplateNode::Dynamic(_) => self
|
TemplateNode::Dynamic(_) => self
|
||||||
.mutations
|
.mutations
|
||||||
.template_mutations
|
.template_edits
|
||||||
.push(CreateStaticPlaceholder {}),
|
.push(CreateStaticPlaceholder {}),
|
||||||
TemplateNode::Text(value) => self
|
TemplateNode::Text(value) => self
|
||||||
.mutations
|
.mutations
|
||||||
.template_mutations
|
.template_edits
|
||||||
.push(CreateStaticText { value }),
|
.push(CreateStaticText { value }),
|
||||||
TemplateNode::DynamicText { .. } => self
|
|
||||||
.mutations
|
TemplateNode::DynamicText { .. } => {
|
||||||
.template_mutations
|
self.mutations.template_edits.push(CreateTextPlaceholder)
|
||||||
.push(CreateTextPlaceholder),
|
}
|
||||||
|
|
||||||
TemplateNode::Element {
|
TemplateNode::Element {
|
||||||
attrs,
|
attrs,
|
||||||
@@ -257,21 +259,19 @@ impl<'b> VirtualDom {
|
|||||||
tag,
|
tag,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
if let Some(namespace) = namespace {
|
match namespace {
|
||||||
self.mutations
|
Some(namespace) => self.mutations.template_edits.push(CreateElementNamespace {
|
||||||
.template_mutations
|
name: tag,
|
||||||
.push(CreateElementNamespace {
|
namespace,
|
||||||
name: tag,
|
}),
|
||||||
namespace,
|
None => self
|
||||||
});
|
.mutations
|
||||||
} else {
|
.template_edits
|
||||||
self.mutations
|
.push(CreateElement { name: tag }),
|
||||||
.template_mutations
|
|
||||||
.push(CreateElement { name: tag });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.mutations
|
self.mutations
|
||||||
.template_mutations
|
.template_edits
|
||||||
.extend(attrs.iter().filter_map(|attr| match attr {
|
.extend(attrs.iter().filter_map(|attr| match attr {
|
||||||
TemplateAttribute::Static {
|
TemplateAttribute::Static {
|
||||||
name,
|
name,
|
||||||
@@ -295,7 +295,7 @@ impl<'b> VirtualDom {
|
|||||||
.for_each(|child| self.create_static_node(child));
|
.for_each(|child| self.create_static_node(child));
|
||||||
|
|
||||||
self.mutations
|
self.mutations
|
||||||
.template_mutations
|
.template_edits
|
||||||
.push(AppendChildren { m: children.len() })
|
.push(AppendChildren { m: children.len() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -404,7 +404,7 @@ impl<'b> VirtualDom {
|
|||||||
) -> usize {
|
) -> usize {
|
||||||
// Keep track of how many mutations are in the buffer in case we need to split them out if a suspense boundary
|
// Keep track of how many mutations are in the buffer in case we need to split them out if a suspense boundary
|
||||||
// is encountered
|
// 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
|
// Create the component's root element
|
||||||
let created = self.create_scope(scope, new);
|
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
|
// Note that we break off dynamic mutations only - since static mutations aren't rendered immediately
|
||||||
let split_off = unsafe {
|
let split_off = unsafe {
|
||||||
std::mem::transmute::<Vec<Mutation>, Vec<Mutation>>(
|
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.created_on_stack.set(created);
|
||||||
boundary
|
boundary
|
||||||
.waiting_on
|
.waiting_on
|
||||||
|
|||||||
+85
-36
@@ -1,57 +1,110 @@
|
|||||||
use bumpalo::boxed::Box as BumpBox;
|
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
fmt::Debug,
|
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct UiEvent<T: 'static + ?Sized> {
|
/// A wrapper around some generic data that handles the event's state
|
||||||
pub(crate) bubbles: Rc<Cell<bool>>,
|
///
|
||||||
|
///
|
||||||
|
/// 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) data: Rc<T>,
|
||||||
|
pub(crate) propogates: Rc<Cell<bool>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UiEvent<dyn Any> {
|
impl<T> Event<T> {
|
||||||
pub fn downcast<T: 'static + Sized>(self) -> Option<UiEvent<T>> {
|
/// Prevent this event from continuing to bubble up the tree to parent elements.
|
||||||
Some(UiEvent {
|
///
|
||||||
bubbles: self.bubbles,
|
/// # Example
|
||||||
data: self.data.downcast().ok()?,
|
///
|
||||||
})
|
/// ```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 {
|
fn clone(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bubbles: self.bubbles.clone(),
|
propogates: self.propogates.clone(),
|
||||||
data: self.data.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>;
|
type Target = Rc<T>;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.data
|
&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 {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("UiEvent")
|
f.debug_struct("UiEvent")
|
||||||
.field("bubble_state", &self.bubbles)
|
.field("bubble_state", &self.propogates)
|
||||||
.field("data", &self.data)
|
.field("data", &self.data)
|
||||||
.finish()
|
.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.
|
/// 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.
|
/// 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
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust, ignore
|
/// ```rust, ignore
|
||||||
///
|
|
||||||
/// rsx!{
|
/// rsx!{
|
||||||
/// MyComponent { onclick: move |evt| log::info!("clicked"), }
|
/// MyComponent { onclick: move |evt| log::info!("clicked") }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// #[derive(Props)]
|
/// #[derive(Props)]
|
||||||
@@ -79,22 +131,17 @@ type ExternalListenerCallback<'bump, T> = BumpBox<'bump, dyn FnMut(T) + 'bump>;
|
|||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
#[derive(Default)]
|
||||||
pub struct EventHandler<'bump, T = ()> {
|
pub struct EventHandler<'bump, T = ()> {
|
||||||
/// The (optional) callback that the user specified
|
pub(super) callback: RefCell<Option<ExternalListenerCallback<'bump, T>>>,
|
||||||
/// Uses a `RefCell` to allow for interior mutability, and FnMut closures.
|
|
||||||
pub callback: RefCell<Option<ExternalListenerCallback<'bump, T>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> Default for EventHandler<'a, T> {
|
type ExternalListenerCallback<'bump, T> = bumpalo::boxed::Box<'bump, dyn FnMut(T) + 'bump>;
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
callback: RefCell::new(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> EventHandler<'_, T> {
|
impl<T> EventHandler<'_, T> {
|
||||||
/// Call this event handler with the appropriate event type
|
/// 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) {
|
pub fn call(&self, event: T) {
|
||||||
if let Some(callback) = self.callback.borrow_mut().as_mut() {
|
if let Some(callback) = self.callback.borrow_mut().as_mut() {
|
||||||
callback(event);
|
callback(event);
|
||||||
@@ -102,6 +149,8 @@ impl<T> EventHandler<'_, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Forcibly drop the internal handler callback, releasing memory
|
/// Forcibly drop the internal handler callback, releasing memory
|
||||||
|
///
|
||||||
|
/// This will force any future calls to "call" to not doing anything
|
||||||
pub fn release(&self) {
|
pub fn release(&self) {
|
||||||
self.callback.replace(None);
|
self.callback.replace(None);
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-106
@@ -1,119 +1,21 @@
|
|||||||
use std::{
|
use crate::{innerlude::DynamicNode, AttributeValue, Element, LazyNodes, ScopeState, VNode};
|
||||||
cell::{Cell, RefCell},
|
|
||||||
fmt::Arguments,
|
|
||||||
};
|
|
||||||
|
|
||||||
use bumpalo::boxed::Box as BumpBox;
|
use bumpalo::boxed::Box as BumpBox;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
|
use std::fmt::Arguments;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
|
|
||||||
use crate::{
|
#[doc(hidden)]
|
||||||
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 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ComponentReturn<'a, A = ()> {
|
pub trait ComponentReturn<'a, A = ()> {
|
||||||
fn into_return(self, cx: &'a ScopeState) -> RenderReturn<'a>;
|
fn into_return(self, cx: &'a ScopeState) -> RenderReturn<'a>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ComponentReturn<'a> for Element<'a> {
|
impl<'a> ComponentReturn<'a> for Element<'a> {
|
||||||
fn into_return(self, _cx: &ScopeState) -> RenderReturn<'a> {
|
fn into_return(self, _cx: &ScopeState) -> RenderReturn<'a> {
|
||||||
RenderReturn::Sync(self)
|
RenderReturn::Sync(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
pub struct AsyncMarker;
|
pub struct AsyncMarker;
|
||||||
impl<'a, F> ComponentReturn<'a, AsyncMarker> for F
|
impl<'a, F> ComponentReturn<'a, AsyncMarker> for F
|
||||||
where
|
where
|
||||||
@@ -142,6 +44,7 @@ impl<'a> RenderReturn<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
pub trait IntoDynNode<'a, A = ()> {
|
pub trait IntoDynNode<'a, A = ()> {
|
||||||
fn into_vnode(self, cx: &'a ScopeState) -> DynamicNode<'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 {
|
impl<'a> IntoDynNode<'_> for &'a str {
|
||||||
fn into_vnode(self, cx: &ScopeState) -> DynamicNode {
|
fn into_vnode(self, cx: &ScopeState) -> DynamicNode {
|
||||||
cx.text(format_args!("{}", self))
|
cx.text_node(format_args!("{}", self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoDynNode<'_> for String {
|
impl IntoDynNode<'_> for String {
|
||||||
fn into_vnode(self, cx: &ScopeState) -> DynamicNode {
|
fn into_vnode(self, cx: &ScopeState) -> DynamicNode {
|
||||||
cx.text(format_args!("{}", self))
|
cx.text_node(format_args!("{}", self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'b> IntoDynNode<'b> for Arguments<'_> {
|
impl<'b> IntoDynNode<'b> for Arguments<'_> {
|
||||||
fn into_vnode(self, cx: &'b ScopeState) -> DynamicNode<'b> {
|
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.
|
// Note that we're using the E as a generic but this is never crafted anyways.
|
||||||
|
#[doc(hidden)]
|
||||||
pub struct FromNodeIterator;
|
pub struct FromNodeIterator;
|
||||||
impl<'a, T, I> IntoDynNode<'a, FromNodeIterator> for T
|
impl<'a, T, I> IntoDynNode<'a, FromNodeIterator> for T
|
||||||
where
|
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
|
//! 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 `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,
|
//! 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.
|
//! 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>,
|
inner: StackNodeStorage<'a, 'b>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type NodeFactory<'a> = &'a ScopeState;
|
|
||||||
|
|
||||||
type StackHeapSize = [usize; 16];
|
type StackHeapSize = [usize; 16];
|
||||||
|
|
||||||
enum StackNodeStorage<'a, 'b> {
|
enum StackNodeStorage<'a, 'b> {
|
||||||
Stack(LazyStack),
|
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> {
|
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
|
/// 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
|
/// is placed on the heap. Most closures will fit into the stack, and is
|
||||||
/// the most optimal way to use the creation function.
|
/// 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
|
// 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 mut slot = Some(val);
|
||||||
|
|
||||||
let val = move |fac: Option<NodeFactory<'a>>| {
|
let val = move |fac: Option<&'a ScopeState>| {
|
||||||
fac.map(
|
fac.map(
|
||||||
slot.take()
|
slot.take()
|
||||||
.expect("LazyNodes closure to be called only once"),
|
.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.
|
/// Create a new [`LazyNodes`] closure, but force it onto the heap.
|
||||||
pub fn new_boxed<F>(inner: F) -> Self
|
pub fn new_boxed<F>(inner: F) -> Self
|
||||||
where
|
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
|
// 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);
|
let mut slot = Some(inner);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
inner: StackNodeStorage::Heap(Box::new(move |fac: Option<NodeFactory<'a>>| {
|
inner: StackNodeStorage::Heap(Box::new(move |fac: Option<&'a ScopeState>| {
|
||||||
fac.map(
|
fac.map(
|
||||||
slot.take()
|
slot.take()
|
||||||
.expect("LazyNodes closure to be called only once"),
|
.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
|
unsafe fn new_inner<F>(val: F) -> Self
|
||||||
where
|
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!(
|
assert_eq!(
|
||||||
ptr as *const u8, &val as *const _ as *const u8,
|
ptr as *const u8, &val as *const _ as *const u8,
|
||||||
@@ -162,12 +160,10 @@ impl<'a, 'b> LazyNodes<'a, 'b> {
|
|||||||
/// ```rust, ignore
|
/// ```rust, ignore
|
||||||
/// let f = LazyNodes::new(move |f| f.element("div", [], [], [] None));
|
/// let f = LazyNodes::new(move |f| f.element("div", [], [], [] None));
|
||||||
///
|
///
|
||||||
/// let fac = NodeFactory::new(&cx);
|
|
||||||
///
|
|
||||||
/// let node = f.call(cac);
|
/// let node = f.call(cac);
|
||||||
/// ```
|
/// ```
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn call(self, f: NodeFactory<'a>) -> VNode<'a> {
|
pub fn call(self, f: &'a ScopeState) -> VNode<'a> {
|
||||||
match self.inner {
|
match self.inner {
|
||||||
StackNodeStorage::Heap(mut lazy) => {
|
StackNodeStorage::Heap(mut lazy) => {
|
||||||
lazy(Some(f)).expect("Closure should not be called twice")
|
lazy(Some(f)).expect("Closure should not be called twice")
|
||||||
@@ -184,18 +180,18 @@ struct LazyStack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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 LazyStack { buf, .. } = self;
|
||||||
let data = buf.as_ref();
|
let data = buf.as_ref();
|
||||||
|
|
||||||
let info_size =
|
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>()
|
/ mem::size_of::<usize>()
|
||||||
- 1;
|
- 1;
|
||||||
|
|
||||||
let info_ofs = data.len() - info_size;
|
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..]) };
|
unsafe { make_fat_ptr(data[..].as_ptr() as usize, &data[info_ofs..]) };
|
||||||
|
|
||||||
self.dropped = true;
|
self.dropped = true;
|
||||||
@@ -210,14 +206,14 @@ impl Drop for LazyStack {
|
|||||||
let LazyStack { buf, .. } = self;
|
let LazyStack { buf, .. } = self;
|
||||||
let data = buf.as_ref();
|
let data = buf.as_ref();
|
||||||
|
|
||||||
let info_size = mem::size_of::<
|
let info_size =
|
||||||
*mut dyn FnMut(Option<NodeFactory<'_>>) -> Option<VNode<'_>>,
|
mem::size_of::<*mut dyn FnMut(Option<&ScopeState>) -> Option<VNode<'_>>>()
|
||||||
>() / mem::size_of::<usize>()
|
/ mem::size_of::<usize>()
|
||||||
- 1;
|
- 1;
|
||||||
|
|
||||||
let info_ofs = data.len() - info_size;
|
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..]) };
|
unsafe { make_fat_ptr(data[..].as_ptr() as usize, &data[info_ofs..]) };
|
||||||
|
|
||||||
self.dropped = true;
|
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 {
|
fn round_to_words(len: usize) -> usize {
|
||||||
(len + mem::size_of::<usize>() - 1) / mem::size_of::<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")]
|
#![doc = include_str!("../README.md")]
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
mod any_props;
|
mod any_props;
|
||||||
mod arena;
|
mod arena;
|
||||||
@@ -67,43 +68,13 @@ pub(crate) mod innerlude {
|
|||||||
/// )
|
/// )
|
||||||
/// ```
|
/// ```
|
||||||
pub type Component<P = ()> = fn(Scope<P>) -> Element;
|
pub type Component<P = ()> = fn(Scope<P>) -> Element;
|
||||||
|
|
||||||
/// A list of attributes
|
|
||||||
pub type Attributes<'a> = Option<&'a [Attribute<'a>]>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use crate::innerlude::{
|
pub use crate::innerlude::{
|
||||||
// AnyAttributeValue, AnyEvent,
|
fc_to_builder, Attribute, AttributeValue, Component, DynamicNode, Element, ElementId, Event,
|
||||||
fc_to_builder,
|
Fragment, LazyNodes, Mutation, Mutations, Properties, RenderReturn, Scope, ScopeId, ScopeState,
|
||||||
Attribute,
|
Scoped, SuspenseBoundary, SuspenseContext, TaskId, Template, TemplateAttribute, TemplateNode,
|
||||||
AttributeValue,
|
VComponent, VNode, VText, VirtualDom,
|
||||||
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,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The purpose of this module is to alleviate imports of many common types
|
/// 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`].
|
/// This includes types like [`Scope`], [`Element`], and [`Component`].
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::innerlude::{
|
pub use crate::innerlude::{
|
||||||
fc_to_builder, Element, EventHandler, Fragment, LazyNodes, NodeFactory, Properties, Scope,
|
fc_to_builder, Element, Event, EventHandler, Fragment, LazyNodes, Properties, Scope,
|
||||||
ScopeId, ScopeState, Scoped, TaskId, Template, TemplateAttribute, TemplateNode, UiEvent,
|
ScopeId, ScopeState, Scoped, TaskId, Template, TemplateAttribute, TemplateNode, VNode,
|
||||||
VNode, VirtualDom,
|
VirtualDom,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,28 +92,4 @@ pub mod exports {
|
|||||||
//! Important dependencies that are used by the rest of the library
|
//! Important dependencies that are used by the rest of the library
|
||||||
//! Feel free to just add the dependencies in your own Crates.toml
|
//! Feel free to just add the dependencies in your own Crates.toml
|
||||||
pub use bumpalo;
|
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};
|
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)]
|
#[derive(Debug, Default)]
|
||||||
#[must_use = "not handling edits can lead to visual inconsistencies in UI"]
|
#[must_use = "not handling edits can lead to visual inconsistencies in UI"]
|
||||||
pub struct Mutations<'a> {
|
pub struct Mutations<'a> {
|
||||||
|
/// The ID of the subtree that these edits are targetting
|
||||||
pub subtree: usize,
|
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> {
|
impl<'a> Mutations<'a> {
|
||||||
/// A useful tool for testing mutations
|
|
||||||
///
|
|
||||||
/// Rewrites IDs to just be "template", so you can compare the mutations
|
/// Rewrites IDs to just be "template", so you can compare the mutations
|
||||||
|
///
|
||||||
|
/// Used really only for testing
|
||||||
pub fn santize(mut self) -> Self {
|
pub fn santize(mut self) -> Self {
|
||||||
for edit in self
|
self.template_edits
|
||||||
.template_mutations
|
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.chain(self.edits.iter_mut())
|
.chain(self.dom_edits.iter_mut())
|
||||||
{
|
.for_each(|edit| match edit {
|
||||||
match edit {
|
|
||||||
Mutation::LoadTemplate { name, .. } => *name = "template",
|
Mutation::LoadTemplate { name, .. } => *name = "template",
|
||||||
Mutation::SaveTemplate { name, .. } => *name = "template",
|
Mutation::SaveTemplate { name, .. } => *name = "template",
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
});
|
||||||
}
|
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> std::ops::Deref for Mutations<'a> {
|
/// Push a new mutation into the dom_edits list
|
||||||
type Target = Vec<Mutation<'a>>;
|
pub(crate) fn push(&mut self, mutation: Mutation<'static>) {
|
||||||
|
self.dom_edits.push(mutation)
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.edits
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::ops::DerefMut for Mutations<'_> {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.edits
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +59,11 @@ each subtree has its own numbering scheme
|
|||||||
)]
|
)]
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum Mutation<'a> {
|
pub enum Mutation<'a> {
|
||||||
|
/// Pop the topmost node from our stack and append them to the node
|
||||||
|
/// at the top of the stack.
|
||||||
AppendChildren {
|
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,
|
m: usize,
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -122,36 +131,61 @@ pub enum Mutation<'a> {
|
|||||||
m: usize,
|
m: usize,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Save the top m nodes as a placeholder
|
||||||
SaveTemplate {
|
SaveTemplate {
|
||||||
|
/// The name of the template that we're saving
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
|
|
||||||
|
/// How many nodes are being saved into this template
|
||||||
m: usize,
|
m: usize,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Set the value of a node's attribute.
|
||||||
SetAttribute {
|
SetAttribute {
|
||||||
|
/// The name of the attribute to set.
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
|
/// The value of the attribute.
|
||||||
value: &'a str,
|
value: &'a str,
|
||||||
|
|
||||||
|
/// The ID of the node to set the attribute of.
|
||||||
id: ElementId,
|
id: ElementId,
|
||||||
|
|
||||||
// value: &'bump str,
|
|
||||||
/// The (optional) namespace of the attribute.
|
/// The (optional) namespace of the attribute.
|
||||||
/// For instance, "style" is in the "style" namespace.
|
/// For instance, "style" is in the "style" namespace.
|
||||||
ns: Option<&'a str>,
|
ns: Option<&'a str>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Set the value of a node's attribute.
|
||||||
SetStaticAttribute {
|
SetStaticAttribute {
|
||||||
|
/// The name of the attribute to set.
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
|
|
||||||
|
/// The value of the attribute.
|
||||||
value: &'a str,
|
value: &'a str,
|
||||||
|
|
||||||
|
/// The (optional) namespace of the attribute.
|
||||||
|
/// For instance, "style" is in the "style" namespace.
|
||||||
ns: Option<&'a str>,
|
ns: Option<&'a str>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Set the value of a node's attribute.
|
||||||
SetBoolAttribute {
|
SetBoolAttribute {
|
||||||
|
/// The name of the attribute to set.
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
|
|
||||||
|
/// The value of the attribute.
|
||||||
value: bool,
|
value: bool,
|
||||||
|
|
||||||
|
/// The ID of the node to set the attribute of.
|
||||||
id: ElementId,
|
id: ElementId,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Set the textcontent of a node.
|
||||||
SetText {
|
SetText {
|
||||||
|
/// The textcontent of the node
|
||||||
value: &'a str,
|
value: &'a str,
|
||||||
|
|
||||||
|
/// The ID of the node to set the textcontent of.
|
||||||
id: ElementId,
|
id: ElementId,
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -175,11 +209,16 @@ pub enum Mutation<'a> {
|
|||||||
/// The ID of the node to remove.
|
/// The ID of the node to remove.
|
||||||
id: ElementId,
|
id: ElementId,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Remove a particular node from the DOM
|
||||||
Remove {
|
Remove {
|
||||||
|
/// The ID of the node to remove.
|
||||||
id: ElementId,
|
id: ElementId,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Push the given root node onto our stack.
|
||||||
PushRoot {
|
PushRoot {
|
||||||
|
/// The ID of the root node to push.
|
||||||
id: ElementId,
|
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 bumpalo::boxed::Box as BumpBox;
|
||||||
use std::{
|
use std::{
|
||||||
any::{Any, TypeId},
|
any::{Any, TypeId},
|
||||||
@@ -70,7 +70,6 @@ pub struct Template<'a> {
|
|||||||
pub attr_paths: &'a [&'a [u8]],
|
pub attr_paths: &'a [&'a [u8]],
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A weird-ish variant of VNodes with way more limited types
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum TemplateNode<'a> {
|
pub enum TemplateNode<'a> {
|
||||||
Element {
|
Element {
|
||||||
@@ -98,7 +97,7 @@ impl<'a> DynamicNode<'a> {
|
|||||||
matches!(self, DynamicNode::Component(_))
|
matches!(self, DynamicNode::Component(_))
|
||||||
}
|
}
|
||||||
pub fn placeholder() -> Self {
|
pub fn placeholder() -> Self {
|
||||||
Self::Placeholder(Cell::new(ElementId(0)))
|
Self::Placeholder(Default::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,18 +155,18 @@ pub enum AttributeValue<'a> {
|
|||||||
None,
|
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> {
|
impl<'a> AttributeValue<'a> {
|
||||||
pub fn new_listener<T: 'static>(
|
pub fn new_listener<T: 'static>(
|
||||||
cx: &'a ScopeState,
|
cx: &'a ScopeState,
|
||||||
mut callback: impl FnMut(UiEvent<T>) + 'a,
|
mut callback: impl FnMut(Event<T>) + 'a,
|
||||||
) -> AttributeValue<'a> {
|
) -> AttributeValue<'a> {
|
||||||
let boxed: BumpBox<'a, dyn FnMut(_) + 'a> = unsafe {
|
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>() {
|
if let Ok(data) = event.data.downcast::<T>() {
|
||||||
callback(UiEvent {
|
callback(Event {
|
||||||
bubbles: event.bubbles,
|
propogates: event.propogates,
|
||||||
data,
|
data,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,25 +15,11 @@ pub type SuspenseContext = Rc<SuspenseBoundary>;
|
|||||||
|
|
||||||
/// Essentially a fiber in React
|
/// Essentially a fiber in React
|
||||||
pub struct SuspenseBoundary {
|
pub struct SuspenseBoundary {
|
||||||
pub id: ScopeId,
|
pub(crate) id: ScopeId,
|
||||||
pub waiting_on: RefCell<HashSet<SuspenseId>>,
|
pub(crate) waiting_on: RefCell<HashSet<SuspenseId>>,
|
||||||
pub mutations: RefCell<Mutations<'static>>,
|
pub(crate) mutations: RefCell<Mutations<'static>>,
|
||||||
pub placeholder: Cell<Option<ElementId>>,
|
pub(crate) placeholder: Cell<Option<ElementId>>,
|
||||||
|
pub(crate) created_on_stack: Cell<usize>,
|
||||||
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()>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SuspenseBoundary {
|
impl SuspenseBoundary {
|
||||||
@@ -44,8 +30,6 @@ impl SuspenseBoundary {
|
|||||||
mutations: RefCell::new(Mutations::default()),
|
mutations: RefCell::new(Mutations::default()),
|
||||||
placeholder: Cell::new(None),
|
placeholder: Cell::new(None),
|
||||||
created_on_stack: Cell::new(0),
|
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 _))
|
.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 scope = &self.scopes[scope.0];
|
||||||
let node = unsafe { scope.previous_frame().try_load_node() };
|
let node = unsafe { scope.previous_frame().try_load_node() };
|
||||||
|
|
||||||
|
|||||||
+102
-8
@@ -1,18 +1,21 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
any_props::AnyProps,
|
any_props::AnyProps,
|
||||||
|
any_props::VProps,
|
||||||
arena::ElementId,
|
arena::ElementId,
|
||||||
bump_frame::BumpFrame,
|
bump_frame::BumpFrame,
|
||||||
factory::RenderReturn,
|
factory::{ComponentReturn, IntoAttributeValue, IntoDynNode, RenderReturn},
|
||||||
|
innerlude::{DynamicNode, EventHandler, VComponent, VText},
|
||||||
innerlude::{Scheduler, SchedulerMsg},
|
innerlude::{Scheduler, SchedulerMsg},
|
||||||
lazynodes::LazyNodes,
|
lazynodes::LazyNodes,
|
||||||
Element, TaskId,
|
Attribute, Element, Properties, TaskId,
|
||||||
};
|
};
|
||||||
use bumpalo::Bump;
|
use bumpalo::{boxed::Box as BumpBox, Bump};
|
||||||
use std::future::Future;
|
|
||||||
use std::{
|
use std::{
|
||||||
any::{Any, TypeId},
|
any::{Any, TypeId},
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
|
fmt::Arguments,
|
||||||
|
future::Future,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::Arc,
|
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)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
|
||||||
pub struct ScopeId(pub usize);
|
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 struct ScopeState {
|
||||||
pub(crate) render_cnt: Cell<usize>,
|
pub(crate) render_cnt: Cell<usize>,
|
||||||
|
|
||||||
@@ -86,7 +89,7 @@ pub struct ScopeState {
|
|||||||
pub(crate) placeholder: Cell<Option<ElementId>>,
|
pub(crate) placeholder: Cell<Option<ElementId>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScopeState {
|
impl<'src> ScopeState {
|
||||||
pub(crate) fn current_frame(&self) -> &BumpFrame {
|
pub(crate) fn current_frame(&self) -> &BumpFrame {
|
||||||
match self.render_cnt.get() % 2 {
|
match self.render_cnt.get() % 2 {
|
||||||
0 => &self.node_arena_1,
|
0 => &self.node_arena_1,
|
||||||
@@ -359,10 +362,101 @@ impl ScopeState {
|
|||||||
/// cx.render(lazy_tree)
|
/// 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))
|
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.
|
/// 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`.
|
/// 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},
|
nodes::{Template, TemplateId},
|
||||||
scheduler::{SuspenseBoundary, SuspenseId},
|
scheduler::{SuspenseBoundary, SuspenseId},
|
||||||
scopes::{ScopeId, ScopeState},
|
scopes::{ScopeId, ScopeState},
|
||||||
AttributeValue, Element, Scope, SuspenseContext, UiEvent,
|
AttributeValue, Element, Event, Scope, SuspenseContext,
|
||||||
};
|
};
|
||||||
use futures_util::{pin_mut, StreamExt};
|
use futures_util::{pin_mut, StreamExt};
|
||||||
use slab::Slab;
|
use slab::Slab;
|
||||||
@@ -350,8 +350,8 @@ impl VirtualDom {
|
|||||||
let mut listeners = vec![];
|
let mut listeners = vec![];
|
||||||
|
|
||||||
// We will clone this later. The data itself is wrapped in RC to be used in callbacks if required
|
// We will clone this later. The data itself is wrapped in RC to be used in callbacks if required
|
||||||
let uievent = UiEvent {
|
let uievent = Event {
|
||||||
bubbles: Rc::new(Cell::new(bubbles)),
|
propogates: Rc::new(Cell::new(bubbles)),
|
||||||
data,
|
data,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -395,7 +395,7 @@ impl VirtualDom {
|
|||||||
cb(uievent.clone());
|
cb(uievent.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
if !uievent.bubbles.get() {
|
if !uievent.propogates.get() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -534,9 +534,12 @@ impl VirtualDom {
|
|||||||
let context = scope.has_context::<SuspenseContext>().unwrap();
|
let context = scope.has_context::<SuspenseContext>().unwrap();
|
||||||
|
|
||||||
self.mutations
|
self.mutations
|
||||||
.extend(context.mutations.borrow_mut().template_mutations.drain(..));
|
.template_edits
|
||||||
|
.extend(context.mutations.borrow_mut().template_edits.drain(..));
|
||||||
|
|
||||||
self.mutations
|
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?
|
// TODO: count how many nodes are on the stack?
|
||||||
self.mutations.push(Mutation::ReplaceWith {
|
self.mutations.push(Mutation::ReplaceWith {
|
||||||
@@ -556,7 +559,7 @@ impl VirtualDom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save the current mutations length so we can split them into boundary
|
// 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
|
// Run the scope and get the mutations
|
||||||
self.run_scope(dirty.id);
|
self.run_scope(dirty.id);
|
||||||
@@ -575,7 +578,8 @@ impl VirtualDom {
|
|||||||
boundary_mut
|
boundary_mut
|
||||||
.mutations
|
.mutations
|
||||||
.borrow_mut()
|
.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
|
// Attach suspended leaves
|
||||||
boundary
|
boundary
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
# Testing of Dioxus core
|
# Testing of Dioxus core
|
||||||
|
|
||||||
|
|
||||||
NodeFactory
|
|
||||||
- [] rsx, html, NodeFactory generate the same structures
|
|
||||||
|
|
||||||
Diffing
|
Diffing
|
||||||
- [x] create elements
|
- [x] create elements
|
||||||
- [x] create text
|
- [x] create text
|
||||||
@@ -19,15 +16,13 @@ Diffing
|
|||||||
- [x] keyed diffing
|
- [x] keyed diffing
|
||||||
- [x] keyed diffing out of order
|
- [x] keyed diffing out of order
|
||||||
- [x] keyed diffing with prefix/suffix
|
- [x] keyed diffing with prefix/suffix
|
||||||
- [x] suspended nodes work
|
- [x] suspended nodes work
|
||||||
|
|
||||||
Lifecycle
|
Lifecycle
|
||||||
- [] Components mount properly
|
- [] Components mount properly
|
||||||
- [] Components create new child components
|
- [] Components create new child components
|
||||||
- [] Replaced components unmount old components and mount new
|
- [] Replaced components unmount old components and mount new
|
||||||
- [] Post-render effects are called
|
- [] Post-render effects are called
|
||||||
- []
|
|
||||||
|
|
||||||
|
|
||||||
Shared Context
|
Shared Context
|
||||||
- [] Shared context propagates downwards
|
- [] Shared context propagates downwards
|
||||||
@@ -37,7 +32,7 @@ Suspense
|
|||||||
- [] use_suspense generates suspended nodes
|
- [] use_suspense generates suspended nodes
|
||||||
|
|
||||||
|
|
||||||
Hooks
|
Hooks
|
||||||
- [] Drop order is maintained
|
- [] Drop order is maintained
|
||||||
- [] Shared hook state is okay
|
- [] Shared hook state is okay
|
||||||
- [] use_hook works
|
- [] use_hook works
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ fn attrs_cycle() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.rebuild().santize().edits,
|
dom.rebuild().santize().dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
||||||
AppendChildren { m: 1 },
|
AppendChildren { m: 1 },
|
||||||
@@ -32,7 +32,7 @@ fn attrs_cycle() {
|
|||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
||||||
AssignId { path: &[0,], id: ElementId(3,) },
|
AssignId { path: &[0,], id: ElementId(3,) },
|
||||||
@@ -44,7 +44,7 @@ fn attrs_cycle() {
|
|||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(3) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(3) },
|
||||||
ReplaceWith { id: ElementId(2), m: 1 }
|
ReplaceWith { id: ElementId(2), m: 1 }
|
||||||
@@ -53,7 +53,7 @@ fn attrs_cycle() {
|
|||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(2) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(2) },
|
||||||
AssignId { path: &[0], id: ElementId(1) },
|
AssignId { path: &[0], id: ElementId(1) },
|
||||||
@@ -66,7 +66,7 @@ fn attrs_cycle() {
|
|||||||
// we take the node taken by attributes since we reused it
|
// we take the node taken by attributes since we reused it
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
||||||
ReplaceWith { id: ElementId(2), m: 1 }
|
ReplaceWith { id: ElementId(2), m: 1 }
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use dioxus::prelude::*;
|
|||||||
fn bool_test() {
|
fn bool_test() {
|
||||||
let mut app = VirtualDom::new(|cx| cx.render(rsx!(div { hidden: false })));
|
let mut app = VirtualDom::new(|cx| cx.render(rsx!(div { hidden: false })));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
app.rebuild().santize().edits,
|
app.rebuild().santize().dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
||||||
SetBoolAttribute { name: "hidden", value: false, id: ElementId(1,) },
|
SetBoolAttribute { name: "hidden", value: false, id: ElementId(1,) },
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ fn test_borrowed_state() {
|
|||||||
let mut dom = VirtualDom::new(Parent);
|
let mut dom = VirtualDom::new(Parent);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.rebuild().santize().edits,
|
dom.rebuild().santize().dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ fn state_shares() {
|
|||||||
|
|
||||||
let mut dom = VirtualDom::new(app);
|
let mut dom = VirtualDom::new(app);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.rebuild().santize().edits,
|
dom.rebuild().santize().dom_edits,
|
||||||
[
|
[
|
||||||
CreateTextNode { value: "Value is 0", id: ElementId(1,) },
|
CreateTextNode { value: "Value is 0", id: ElementId(1,) },
|
||||||
AppendChildren { m: 1 },
|
AppendChildren { m: 1 },
|
||||||
@@ -37,7 +37,7 @@ fn state_shares() {
|
|||||||
|
|
||||||
dom.mark_dirty(ScopeId(2));
|
dom.mark_dirty(ScopeId(2));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[SetText { value: "Value is 2", id: ElementId(1,) },]
|
[SetText { value: "Value is 2", id: ElementId(1,) },]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ fn state_shares() {
|
|||||||
dom.mark_dirty(ScopeId(2));
|
dom.mark_dirty(ScopeId(2));
|
||||||
let edits = dom.render_immediate();
|
let edits = dom.render_immediate();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
edits.santize().edits,
|
edits.santize().dom_edits,
|
||||||
[SetText { value: "Value is 3", id: ElementId(1,) },]
|
[SetText { value: "Value is 3", id: ElementId(1,) },]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -23,7 +23,7 @@ fn test_original_diff() {
|
|||||||
|
|
||||||
let edits = dom.rebuild().santize();
|
let edits = dom.rebuild().santize();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
edits.template_mutations,
|
edits.template_edits,
|
||||||
[
|
[
|
||||||
// create template
|
// create template
|
||||||
CreateElement { name: "div" },
|
CreateElement { name: "div" },
|
||||||
@@ -36,7 +36,7 @@ fn test_original_diff() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
edits.edits,
|
edits.dom_edits,
|
||||||
[
|
[
|
||||||
// add to root
|
// add to root
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
||||||
@@ -67,7 +67,7 @@ fn create() {
|
|||||||
|
|
||||||
let edits = dom.rebuild().santize();
|
let edits = dom.rebuild().santize();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
edits.template_mutations,
|
edits.template_edits,
|
||||||
[
|
[
|
||||||
// create template
|
// create template
|
||||||
CreateElement { name: "div" },
|
CreateElement { name: "div" },
|
||||||
@@ -99,7 +99,7 @@ fn create_list() {
|
|||||||
|
|
||||||
let edits = dom.rebuild().santize();
|
let edits = dom.rebuild().santize();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
edits.template_mutations,
|
edits.template_edits,
|
||||||
[
|
[
|
||||||
// create template
|
// create template
|
||||||
CreateElement { name: "div" },
|
CreateElement { name: "div" },
|
||||||
@@ -123,7 +123,7 @@ fn create_simple() {
|
|||||||
|
|
||||||
let edits = dom.rebuild().santize();
|
let edits = dom.rebuild().santize();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
edits.template_mutations,
|
edits.template_edits,
|
||||||
[
|
[
|
||||||
// create template
|
// create template
|
||||||
CreateElement { name: "div" },
|
CreateElement { name: "div" },
|
||||||
@@ -160,7 +160,7 @@ fn create_components() {
|
|||||||
|
|
||||||
let edits = dom.rebuild().santize();
|
let edits = dom.rebuild().santize();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
edits.template_mutations,
|
edits.template_edits,
|
||||||
[
|
[
|
||||||
// The "child" template
|
// The "child" template
|
||||||
CreateElement { name: "h1" },
|
CreateElement { name: "h1" },
|
||||||
@@ -196,7 +196,7 @@ fn anchors() {
|
|||||||
// note that the template under "false" doesn't show up since it's not loaded
|
// note that the template under "false" doesn't show up since it's not loaded
|
||||||
let edits = dom.rebuild().santize();
|
let edits = dom.rebuild().santize();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
edits.template_mutations,
|
edits.template_edits,
|
||||||
[
|
[
|
||||||
// create each template
|
// create each template
|
||||||
CreateElement { name: "div" },
|
CreateElement { name: "div" },
|
||||||
@@ -207,7 +207,7 @@ fn anchors() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
edits.edits,
|
edits.dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
||||||
CreatePlaceholder { id: ElementId(2) },
|
CreatePlaceholder { id: ElementId(2) },
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ fn multiroot() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.rebuild().santize().template_mutations,
|
dom.rebuild().santize().template_edits,
|
||||||
[
|
[
|
||||||
CreateElement { name: "div" },
|
CreateElement { name: "div" },
|
||||||
CreateStaticText { value: "Hello a" },
|
CreateStaticText { value: "Hello a" },
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ fn empty_fragment_creates_nothing() {
|
|||||||
let edits = vdom.rebuild();
|
let edits = vdom.rebuild();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
edits.edits,
|
edits.dom_edits,
|
||||||
[
|
[
|
||||||
CreatePlaceholder { id: ElementId(1) },
|
CreatePlaceholder { id: ElementId(1) },
|
||||||
AppendChildren { m: 1 }
|
AppendChildren { m: 1 }
|
||||||
@@ -32,7 +32,7 @@ fn root_fragments_work() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
vdom.rebuild().edits.last().unwrap(),
|
vdom.rebuild().dom_edits.last().unwrap(),
|
||||||
&AppendChildren { m: 2 }
|
&AppendChildren { m: 2 }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -59,7 +59,7 @@ fn fragments_nested() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
vdom.rebuild().edits.last().unwrap(),
|
vdom.rebuild().dom_edits.last().unwrap(),
|
||||||
&AppendChildren { m: 8 }
|
&AppendChildren { m: 8 }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -84,7 +84,7 @@ fn fragments_across_components() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
VirtualDom::new(app).rebuild().edits.last().unwrap(),
|
VirtualDom::new(app).rebuild().dom_edits.last().unwrap(),
|
||||||
&AppendChildren { m: 8 }
|
&AppendChildren { m: 8 }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -98,7 +98,7 @@ fn list_fragments() {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
VirtualDom::new(app).rebuild().edits.last().unwrap(),
|
VirtualDom::new(app).rebuild().dom_edits.last().unwrap(),
|
||||||
&AppendChildren { m: 7 }
|
&AppendChildren { m: 7 }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,7 @@ fn list_renders() {
|
|||||||
let edits = dom.rebuild().santize();
|
let edits = dom.rebuild().santize();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
edits.template_mutations,
|
edits.template_edits,
|
||||||
[
|
[
|
||||||
// Create the outer div
|
// Create the outer div
|
||||||
CreateElement { name: "div" },
|
CreateElement { name: "div" },
|
||||||
@@ -51,7 +51,7 @@ fn list_renders() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
edits.edits,
|
edits.dom_edits,
|
||||||
[
|
[
|
||||||
// Load the outer div
|
// Load the outer div
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ fn nested_passthru_creates() {
|
|||||||
let edits = dom.rebuild().santize();
|
let edits = dom.rebuild().santize();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
edits.edits,
|
edits.dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
||||||
AppendChildren { m: 1 },
|
AppendChildren { m: 1 },
|
||||||
@@ -64,7 +64,7 @@ fn nested_passthru_creates_add() {
|
|||||||
let mut dom = VirtualDom::new(app);
|
let mut dom = VirtualDom::new(app);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.rebuild().santize().edits,
|
dom.rebuild().santize().dom_edits,
|
||||||
[
|
[
|
||||||
// load 1
|
// load 1
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
||||||
@@ -92,11 +92,11 @@ fn dynamic_node_as_root() {
|
|||||||
let edits = dom.rebuild().santize();
|
let edits = dom.rebuild().santize();
|
||||||
|
|
||||||
// Since the roots were all dynamic, they should not cause any template muations
|
// Since the roots were all dynamic, they should not cause any template muations
|
||||||
assert_eq!(edits.template_mutations, []);
|
assert_eq!(edits.template_edits, []);
|
||||||
|
|
||||||
// The root node is text, so we just create it on the spot
|
// The root node is text, so we just create it on the spot
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
edits.edits,
|
edits.dom_edits,
|
||||||
[
|
[
|
||||||
CreateTextNode { value: "123", id: ElementId(1) },
|
CreateTextNode { value: "123", id: ElementId(1) },
|
||||||
CreateTextNode { value: "456", id: ElementId(2) },
|
CreateTextNode { value: "456", id: ElementId(2) },
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ fn cycling_elements() {
|
|||||||
|
|
||||||
let edits = dom.rebuild().santize();
|
let edits = dom.rebuild().santize();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
edits.edits,
|
edits.dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
||||||
AppendChildren { m: 1 },
|
AppendChildren { m: 1 },
|
||||||
@@ -23,7 +23,7 @@ fn cycling_elements() {
|
|||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
||||||
ReplaceWith { id: ElementId(1,), m: 1 },
|
ReplaceWith { id: ElementId(1,), m: 1 },
|
||||||
@@ -33,7 +33,7 @@ fn cycling_elements() {
|
|||||||
// notice that the IDs cycle back to ElementId(1), preserving a minimal memory footprint
|
// notice that the IDs cycle back to ElementId(1), preserving a minimal memory footprint
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
||||||
ReplaceWith { id: ElementId(2,), m: 1 },
|
ReplaceWith { id: ElementId(2,), m: 1 },
|
||||||
@@ -42,7 +42,7 @@ fn cycling_elements() {
|
|||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
||||||
ReplaceWith { id: ElementId(1,), m: 1 },
|
ReplaceWith { id: ElementId(1,), m: 1 },
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ fn component_swap() {
|
|||||||
let mut dom = VirtualDom::new(app);
|
let mut dom = VirtualDom::new(app);
|
||||||
let edits = dom.rebuild().santize();
|
let edits = dom.rebuild().santize();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
edits.edits,
|
edits.dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(2) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(2) },
|
||||||
@@ -75,7 +75,7 @@ fn component_swap() {
|
|||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(6) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(6) },
|
||||||
ReplaceWith { id: ElementId(5), m: 1 }
|
ReplaceWith { id: ElementId(5), m: 1 }
|
||||||
@@ -84,7 +84,7 @@ fn component_swap() {
|
|||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(5) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(5) },
|
||||||
ReplaceWith { id: ElementId(6), m: 1 }
|
ReplaceWith { id: ElementId(6), m: 1 }
|
||||||
@@ -93,7 +93,7 @@ fn component_swap() {
|
|||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(6) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(6) },
|
||||||
ReplaceWith { id: ElementId(5), m: 1 }
|
ReplaceWith { id: ElementId(5), m: 1 }
|
||||||
|
|||||||
@@ -14,19 +14,19 @@ fn text_diff() {
|
|||||||
|
|
||||||
vdom.mark_dirty(ScopeId(0));
|
vdom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
vdom.render_immediate().edits,
|
vdom.render_immediate().dom_edits,
|
||||||
[SetText { value: "hello 1", id: ElementId(2) }]
|
[SetText { value: "hello 1", id: ElementId(2) }]
|
||||||
);
|
);
|
||||||
|
|
||||||
vdom.mark_dirty(ScopeId(0));
|
vdom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
vdom.render_immediate().edits,
|
vdom.render_immediate().dom_edits,
|
||||||
[SetText { value: "hello 2", id: ElementId(2) }]
|
[SetText { value: "hello 2", id: ElementId(2) }]
|
||||||
);
|
);
|
||||||
|
|
||||||
vdom.mark_dirty(ScopeId(0));
|
vdom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
vdom.render_immediate().edits,
|
vdom.render_immediate().dom_edits,
|
||||||
[SetText { value: "hello 3", id: ElementId(2) }]
|
[SetText { value: "hello 3", id: ElementId(2) }]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -48,7 +48,7 @@ fn element_swap() {
|
|||||||
|
|
||||||
vdom.mark_dirty(ScopeId(0));
|
vdom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
vdom.render_immediate().santize().edits,
|
vdom.render_immediate().santize().dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
||||||
ReplaceWith { id: ElementId(1,), m: 1 },
|
ReplaceWith { id: ElementId(1,), m: 1 },
|
||||||
@@ -57,7 +57,7 @@ fn element_swap() {
|
|||||||
|
|
||||||
vdom.mark_dirty(ScopeId(0));
|
vdom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
vdom.render_immediate().santize().edits,
|
vdom.render_immediate().santize().dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
||||||
ReplaceWith { id: ElementId(2,), m: 1 },
|
ReplaceWith { id: ElementId(2,), m: 1 },
|
||||||
@@ -66,7 +66,7 @@ fn element_swap() {
|
|||||||
|
|
||||||
vdom.mark_dirty(ScopeId(0));
|
vdom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
vdom.render_immediate().santize().edits,
|
vdom.render_immediate().santize().dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
||||||
ReplaceWith { id: ElementId(1,), m: 1 },
|
ReplaceWith { id: ElementId(1,), m: 1 },
|
||||||
@@ -75,7 +75,7 @@ fn element_swap() {
|
|||||||
|
|
||||||
vdom.mark_dirty(ScopeId(0));
|
vdom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
vdom.render_immediate().santize().edits,
|
vdom.render_immediate().santize().dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
||||||
ReplaceWith { id: ElementId(2,), m: 1 },
|
ReplaceWith { id: ElementId(2,), m: 1 },
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ fn keyed_diffing_out_of_order() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.rebuild().santize().edits,
|
dom.rebuild().santize().dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
||||||
@@ -39,7 +39,7 @@ fn keyed_diffing_out_of_order() {
|
|||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().edits,
|
dom.render_immediate().dom_edits,
|
||||||
[
|
[
|
||||||
PushRoot { id: ElementId(7,) },
|
PushRoot { id: ElementId(7,) },
|
||||||
InsertBefore { id: ElementId(5,), m: 1 },
|
InsertBefore { id: ElementId(5,), m: 1 },
|
||||||
@@ -64,7 +64,7 @@ fn keyed_diffing_out_of_order_adds() {
|
|||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().edits,
|
dom.render_immediate().dom_edits,
|
||||||
[
|
[
|
||||||
PushRoot { id: ElementId(5,) },
|
PushRoot { id: ElementId(5,) },
|
||||||
PushRoot { id: ElementId(4,) },
|
PushRoot { id: ElementId(4,) },
|
||||||
@@ -90,7 +90,7 @@ fn keyed_diffing_out_of_order_adds_3() {
|
|||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().edits,
|
dom.render_immediate().dom_edits,
|
||||||
[
|
[
|
||||||
PushRoot { id: ElementId(5,) },
|
PushRoot { id: ElementId(5,) },
|
||||||
PushRoot { id: ElementId(4,) },
|
PushRoot { id: ElementId(4,) },
|
||||||
@@ -116,7 +116,7 @@ fn keyed_diffing_out_of_order_adds_4() {
|
|||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().edits,
|
dom.render_immediate().dom_edits,
|
||||||
[
|
[
|
||||||
PushRoot { id: ElementId(5,) },
|
PushRoot { id: ElementId(5,) },
|
||||||
PushRoot { id: ElementId(4,) },
|
PushRoot { id: ElementId(4,) },
|
||||||
@@ -142,7 +142,7 @@ fn keyed_diffing_out_of_order_adds_5() {
|
|||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().edits,
|
dom.render_immediate().dom_edits,
|
||||||
[
|
[
|
||||||
PushRoot { id: ElementId(5,) },
|
PushRoot { id: ElementId(5,) },
|
||||||
InsertBefore { id: ElementId(4,), m: 1 },
|
InsertBefore { id: ElementId(4,), m: 1 },
|
||||||
@@ -167,7 +167,7 @@ fn keyed_diffing_additions() {
|
|||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(6) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(6) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(7) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(7) },
|
||||||
@@ -192,7 +192,7 @@ fn keyed_diffing_additions_and_moves_on_ends() {
|
|||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[
|
[
|
||||||
// create 11, 12
|
// create 11, 12
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(5) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(5) },
|
||||||
@@ -222,7 +222,7 @@ fn keyed_diffing_additions_and_moves_in_middle() {
|
|||||||
// LIS: 4, 5, 6
|
// LIS: 4, 5, 6
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[
|
[
|
||||||
// create 5, 6
|
// create 5, 6
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(5) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(5) },
|
||||||
@@ -256,7 +256,7 @@ fn controlled_keyed_diffing_out_of_order() {
|
|||||||
// LIS: 5, 6
|
// LIS: 5, 6
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[
|
[
|
||||||
// remove 7
|
// remove 7
|
||||||
Remove { id: ElementId(4,) },
|
Remove { id: ElementId(4,) },
|
||||||
@@ -289,7 +289,7 @@ fn controlled_keyed_diffing_out_of_order_max_test() {
|
|||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[
|
[
|
||||||
Remove { id: ElementId(5,) },
|
Remove { id: ElementId(5,) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(5) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(5) },
|
||||||
@@ -318,7 +318,7 @@ fn remove_list() {
|
|||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[
|
[
|
||||||
Remove { id: ElementId(3) },
|
Remove { id: ElementId(3) },
|
||||||
Remove { id: ElementId(4) },
|
Remove { id: ElementId(4) },
|
||||||
@@ -343,7 +343,7 @@ fn no_common_keys() {
|
|||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[
|
[
|
||||||
Remove { id: ElementId(2) },
|
Remove { id: ElementId(2) },
|
||||||
Remove { id: ElementId(3) },
|
Remove { id: ElementId(3) },
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ fn list_creates_one_by_one() {
|
|||||||
|
|
||||||
// load the div and then assign the empty fragment as a placeholder
|
// load the div and then assign the empty fragment as a placeholder
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.rebuild().santize().edits,
|
dom.rebuild().santize().dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
||||||
AssignId { path: &[0], id: ElementId(2,) },
|
AssignId { path: &[0], id: ElementId(2,) },
|
||||||
@@ -28,7 +28,7 @@ fn list_creates_one_by_one() {
|
|||||||
// Rendering the first item should replace the placeholder with an element
|
// Rendering the first item should replace the placeholder with an element
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(3,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(3,) },
|
||||||
HydrateText { path: &[0], value: "0", id: ElementId(4,) },
|
HydrateText { path: &[0], value: "0", id: ElementId(4,) },
|
||||||
@@ -39,7 +39,7 @@ fn list_creates_one_by_one() {
|
|||||||
// Rendering the next item should insert after the previous
|
// Rendering the next item should insert after the previous
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
||||||
HydrateText { path: &[0], value: "1", id: ElementId(5,) },
|
HydrateText { path: &[0], value: "1", id: ElementId(5,) },
|
||||||
@@ -50,7 +50,7 @@ fn list_creates_one_by_one() {
|
|||||||
// ... and again!
|
// ... and again!
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(6,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(6,) },
|
||||||
HydrateText { path: &[0], value: "2", id: ElementId(7,) },
|
HydrateText { path: &[0], value: "2", id: ElementId(7,) },
|
||||||
@@ -61,7 +61,7 @@ fn list_creates_one_by_one() {
|
|||||||
// once more
|
// once more
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(8,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(8,) },
|
||||||
HydrateText { path: &[0], value: "3", id: ElementId(9,) },
|
HydrateText { path: &[0], value: "3", id: ElementId(9,) },
|
||||||
@@ -86,7 +86,7 @@ fn removes_one_by_one() {
|
|||||||
|
|
||||||
// load the div and then assign the empty fragment as a placeholder
|
// load the div and then assign the empty fragment as a placeholder
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.rebuild().santize().edits,
|
dom.rebuild().santize().dom_edits,
|
||||||
[
|
[
|
||||||
// The container
|
// The container
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
||||||
@@ -108,14 +108,14 @@ fn removes_one_by_one() {
|
|||||||
// Rendering the first item should replace the placeholder with an element
|
// Rendering the first item should replace the placeholder with an element
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[Remove { id: ElementId(6) }]
|
[Remove { id: ElementId(6) }]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Remove div(2)
|
// Remove div(2)
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[Remove { id: ElementId(4) }]
|
[Remove { id: ElementId(4) }]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@ fn removes_one_by_one() {
|
|||||||
// todo: this should just be a remove with no placeholder
|
// todo: this should just be a remove with no placeholder
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[
|
[
|
||||||
CreatePlaceholder { id: ElementId(3) },
|
CreatePlaceholder { id: ElementId(3) },
|
||||||
ReplaceWith { id: ElementId(2), m: 1 }
|
ReplaceWith { id: ElementId(2), m: 1 }
|
||||||
@@ -134,7 +134,7 @@ fn removes_one_by_one() {
|
|||||||
// todo: this should actually be append to, but replace placeholder is fine for now
|
// todo: this should actually be append to, but replace placeholder is fine for now
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(2) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(2) },
|
||||||
HydrateText { path: &[0], value: "0", id: ElementId(4) },
|
HydrateText { path: &[0], value: "0", id: ElementId(4) },
|
||||||
@@ -161,7 +161,7 @@ fn list_shrink_multiroot() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.rebuild().santize().edits,
|
dom.rebuild().santize().dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
||||||
AssignId { path: &[0,], id: ElementId(2,) },
|
AssignId { path: &[0,], id: ElementId(2,) },
|
||||||
@@ -171,7 +171,7 @@ fn list_shrink_multiroot() {
|
|||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(3) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(3) },
|
||||||
HydrateText { path: &[0], value: "0", id: ElementId(4) },
|
HydrateText { path: &[0], value: "0", id: ElementId(4) },
|
||||||
@@ -183,7 +183,7 @@ fn list_shrink_multiroot() {
|
|||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(2) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(2) },
|
||||||
HydrateText { path: &[0], value: "1", id: ElementId(7) },
|
HydrateText { path: &[0], value: "1", id: ElementId(7) },
|
||||||
@@ -195,7 +195,7 @@ fn list_shrink_multiroot() {
|
|||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(10) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(10) },
|
||||||
HydrateText { path: &[0], value: "2", id: ElementId(11) },
|
HydrateText { path: &[0], value: "2", id: ElementId(11) },
|
||||||
@@ -223,7 +223,7 @@ fn removes_one_by_one_multiroot() {
|
|||||||
|
|
||||||
// load the div and then assign the empty fragment as a placeholder
|
// load the div and then assign the empty fragment as a placeholder
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.rebuild().santize().edits,
|
dom.rebuild().santize().dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
||||||
//
|
//
|
||||||
@@ -250,19 +250,19 @@ fn removes_one_by_one_multiroot() {
|
|||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[Remove { id: ElementId(10) }, Remove { id: ElementId(12) }]
|
[Remove { id: ElementId(10) }, Remove { id: ElementId(12) }]
|
||||||
);
|
);
|
||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[Remove { id: ElementId(6) }, Remove { id: ElementId(8) }]
|
[Remove { id: ElementId(6) }, Remove { id: ElementId(8) }]
|
||||||
);
|
);
|
||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.render_immediate().santize().edits,
|
dom.render_immediate().santize().dom_edits,
|
||||||
[
|
[
|
||||||
Remove { id: ElementId(4) },
|
Remove { id: ElementId(4) },
|
||||||
CreatePlaceholder { id: ElementId(5) },
|
CreatePlaceholder { id: ElementId(5) },
|
||||||
@@ -282,7 +282,7 @@ fn two_equal_fragments_are_equal_static() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
_ = dom.rebuild();
|
_ = dom.rebuild();
|
||||||
assert!(dom.render_immediate().edits.is_empty());
|
assert!(dom.render_immediate().dom_edits.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -296,7 +296,7 @@ fn two_equal_fragments_are_equal() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
_ = dom.rebuild();
|
_ = dom.rebuild();
|
||||||
assert!(dom.render_immediate().edits.is_empty());
|
assert!(dom.render_immediate().dom_edits.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -315,9 +315,9 @@ fn remove_many() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let edits = dom.rebuild().santize();
|
let edits = dom.rebuild().santize();
|
||||||
assert!(edits.template_mutations.is_empty());
|
assert!(edits.template_edits.is_empty());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
edits.edits,
|
edits.dom_edits,
|
||||||
[
|
[
|
||||||
CreatePlaceholder { id: ElementId(1,) },
|
CreatePlaceholder { id: ElementId(1,) },
|
||||||
AppendChildren { m: 1 },
|
AppendChildren { m: 1 },
|
||||||
@@ -327,7 +327,7 @@ fn remove_many() {
|
|||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
let edits = dom.render_immediate().santize();
|
let edits = dom.render_immediate().santize();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
edits.edits,
|
edits.dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
||||||
HydrateText { path: &[0,], value: "hello 0", id: ElementId(3,) },
|
HydrateText { path: &[0,], value: "hello 0", id: ElementId(3,) },
|
||||||
@@ -338,7 +338,7 @@ fn remove_many() {
|
|||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
let edits = dom.render_immediate().santize();
|
let edits = dom.render_immediate().santize();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
edits.edits,
|
edits.dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
||||||
HydrateText { path: &[0,], value: "hello 1", id: ElementId(4,) },
|
HydrateText { path: &[0,], value: "hello 1", id: ElementId(4,) },
|
||||||
@@ -355,7 +355,7 @@ fn remove_many() {
|
|||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
let edits = dom.render_immediate().santize();
|
let edits = dom.render_immediate().santize();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
edits.edits,
|
edits.dom_edits,
|
||||||
[
|
[
|
||||||
Remove { id: ElementId(1,) },
|
Remove { id: ElementId(1,) },
|
||||||
Remove { id: ElementId(5,) },
|
Remove { id: ElementId(5,) },
|
||||||
@@ -369,7 +369,7 @@ fn remove_many() {
|
|||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
let edits = dom.render_immediate().santize();
|
let edits = dom.render_immediate().santize();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
edits.edits,
|
edits.dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
||||||
HydrateText { path: &[0,], value: "hello 0", id: ElementId(10,) },
|
HydrateText { path: &[0,], value: "hello 0", id: ElementId(10,) },
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ fn dual_stream() {
|
|||||||
|
|
||||||
use Mutation::*;
|
use Mutation::*;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
edits.template_mutations,
|
edits.template_edits,
|
||||||
[
|
[
|
||||||
CreateElement { name: "div" },
|
CreateElement { name: "div" },
|
||||||
SetStaticAttribute { name: "class", value: "asd", ns: None },
|
SetStaticAttribute { name: "class", value: "asd", ns: None },
|
||||||
@@ -66,7 +66,7 @@ fn dual_stream() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
edits.edits,
|
edits.dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
||||||
SetAttribute { name: "class", value: "123", id: ElementId(1), ns: None },
|
SetAttribute { name: "class", value: "123", id: ElementId(1), ns: None },
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ fn manual_diffing() {
|
|||||||
*value.lock().unwrap() = "goodbye";
|
*value.lock().unwrap() = "goodbye";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.rebuild().santize().edits,
|
dom.rebuild().santize().dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(3) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(3) },
|
||||||
HydrateText { path: &[0], value: "goodbye", id: ElementId(4) },
|
HydrateText { path: &[0], value: "goodbye", id: ElementId(4) },
|
||||||
@@ -62,7 +62,7 @@ fn events_generate() {
|
|||||||
let edits = dom.render_immediate();
|
let edits = dom.render_immediate();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
edits.edits,
|
edits.dom_edits,
|
||||||
[
|
[
|
||||||
CreatePlaceholder { id: ElementId(2) },
|
CreatePlaceholder { id: ElementId(2) },
|
||||||
ReplaceWith { id: ElementId(1), m: 1 }
|
ReplaceWith { id: ElementId(1), m: 1 }
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ async fn it_works() {
|
|||||||
|
|
||||||
// We should at least get the top-level template in before pausing for the children
|
// We should at least get the top-level template in before pausing for the children
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mutations.template_mutations,
|
mutations.template_edits,
|
||||||
[
|
[
|
||||||
CreateElement { name: "div" },
|
CreateElement { name: "div" },
|
||||||
CreateStaticText { value: "Waiting for child..." },
|
CreateStaticText { value: "Waiting for child..." },
|
||||||
@@ -25,7 +25,7 @@ async fn it_works() {
|
|||||||
|
|
||||||
// And we should load it in and assign the placeholder properly
|
// And we should load it in and assign the placeholder properly
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mutations.edits,
|
mutations.dom_edits,
|
||||||
[
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
||||||
// hmmmmmmmmm.... with suspense how do we guarantee that IDs increase linearly?
|
// hmmmmmmmmm.... with suspense how do we guarantee that IDs increase linearly?
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ impl DesktopController {
|
|||||||
{
|
{
|
||||||
let edits = dom.rebuild();
|
let edits = dom.rebuild();
|
||||||
let mut queue = edit_queue.lock().unwrap();
|
let mut queue = edit_queue.lock().unwrap();
|
||||||
queue.push(serde_json::to_string(&edits.template_mutations).unwrap());
|
queue.push(serde_json::to_string(&edits.template_edits).unwrap());
|
||||||
queue.push(serde_json::to_string(&edits.edits).unwrap());
|
queue.push(serde_json::to_string(&edits.dom_edits).unwrap());
|
||||||
proxy.send_event(UserWindowEvent::EditsReady).unwrap();
|
proxy.send_event(UserWindowEvent::EditsReady).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,8 +79,8 @@ impl DesktopController {
|
|||||||
|
|
||||||
{
|
{
|
||||||
let mut queue = edit_queue.lock().unwrap();
|
let mut queue = edit_queue.lock().unwrap();
|
||||||
queue.push(serde_json::to_string(&muts.template_mutations).unwrap());
|
queue.push(serde_json::to_string(&muts.template_edits).unwrap());
|
||||||
queue.push(serde_json::to_string(&muts.edits).unwrap());
|
queue.push(serde_json::to_string(&muts.dom_edits).unwrap());
|
||||||
let _ = proxy.send_event(UserWindowEvent::EditsReady);
|
let _ = proxy.send_event(UserWindowEvent::EditsReady);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ fn create_rows(c: &mut Criterion) {
|
|||||||
|
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
let g = dom.rebuild();
|
let g = dom.rebuild();
|
||||||
assert!(g.edits.len() > 1);
|
assert!(g.dom_edits.len() > 1);
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,25 @@
|
|||||||
// #![deny(missing_docs)]
|
#[macro_export]
|
||||||
//! Useful foundational hooks for Dioxus
|
/// 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();
|
||||||
|
)*}
|
||||||
|
}
|
||||||
|
|
||||||
mod usestate;
|
mod usestate;
|
||||||
pub use usestate::{use_state, UseState};
|
pub use usestate::{use_state, UseState};
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ pub fn use_state<T: 'static>(
|
|||||||
let update_callback = cx.schedule_update();
|
let update_callback = cx.schedule_update();
|
||||||
let slot = Rc::new(RefCell::new(current_val.clone()));
|
let slot = Rc::new(RefCell::new(current_val.clone()));
|
||||||
let setter = Rc::new({
|
let setter = Rc::new({
|
||||||
dioxus_core::to_owned![update_callback, slot];
|
to_owned![update_callback, slot];
|
||||||
move |new| {
|
move |new| {
|
||||||
{
|
{
|
||||||
let mut slot = slot.borrow_mut();
|
let mut slot = slot.borrow_mut();
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ macro_rules! impl_event {
|
|||||||
) => {
|
) => {
|
||||||
$(
|
$(
|
||||||
$( #[$attr] )*
|
$( #[$attr] )*
|
||||||
pub fn $name<'a>(_cx: &'a ::dioxus_core::ScopeState, _f: impl FnMut(::dioxus_core::UiEvent<$data>) + 'a) -> ::dioxus_core::Attribute<'a> {
|
pub fn $name<'a>(_cx: &'a ::dioxus_core::ScopeState, _f: impl FnMut(::dioxus_core::Event<$data>) + 'a) -> ::dioxus_core::Attribute<'a> {
|
||||||
::dioxus_core::Attribute {
|
::dioxus_core::Attribute {
|
||||||
name: stringify!($name),
|
name: stringify!($name),
|
||||||
value: ::dioxus_core::AttributeValue::new_listener(_cx, _f),
|
value: ::dioxus_core::AttributeValue::new_listener(_cx, _f),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use dioxus_core::UiEvent;
|
use dioxus_core::Event;
|
||||||
|
|
||||||
pub type AnimationEvent = UiEvent<AnimationData>;
|
pub type AnimationEvent = Event<AnimationData>;
|
||||||
|
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use dioxus_core::UiEvent;
|
use dioxus_core::Event;
|
||||||
|
|
||||||
pub type ClipboardEvent = UiEvent<ClipboardData>;
|
pub type ClipboardEvent = Event<ClipboardData>;
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ClipboardData {
|
pub struct ClipboardData {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use dioxus_core::UiEvent;
|
use dioxus_core::Event;
|
||||||
|
|
||||||
pub type CompositionEvent = UiEvent<CompositionData>;
|
pub type CompositionEvent = Event<CompositionData>;
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct CompositionData {
|
pub struct CompositionData {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
|
||||||
use dioxus_core::UiEvent;
|
use dioxus_core::Event;
|
||||||
|
|
||||||
use crate::MouseData;
|
use crate::MouseData;
|
||||||
|
|
||||||
pub type DragEvent = UiEvent<DragData>;
|
pub type DragEvent = Event<DragData>;
|
||||||
|
|
||||||
/// The DragEvent interface is a DOM event that represents a drag and drop interaction. The user initiates a drag by
|
/// The DragEvent interface is a DOM event that represents a drag and drop interaction. The user initiates a drag by
|
||||||
/// placing a pointer device (such as a mouse) on the touch surface and then dragging the pointer to a new location
|
/// placing a pointer device (such as a mouse) on the touch surface and then dragging the pointer to a new location
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use dioxus_core::UiEvent;
|
use dioxus_core::Event;
|
||||||
|
|
||||||
pub type FocusEvent = UiEvent<FocusData>;
|
pub type FocusEvent = Event<FocusData>;
|
||||||
|
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use std::{collections::HashMap, fmt::Debug, sync::Arc};
|
use std::{collections::HashMap, fmt::Debug, sync::Arc};
|
||||||
|
|
||||||
use dioxus_core::UiEvent;
|
use dioxus_core::Event;
|
||||||
|
|
||||||
pub type FormEvent = UiEvent<FormData>;
|
pub type FormEvent = Event<FormData>;
|
||||||
|
|
||||||
/* DOMEvent: Send + SyncTarget relatedTarget */
|
/* DOMEvent: Send + SyncTarget relatedTarget */
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use dioxus_core::UiEvent;
|
use dioxus_core::Event;
|
||||||
|
|
||||||
pub type ImageEvent = UiEvent<ImageData>;
|
pub type ImageEvent = Event<ImageData>;
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ImageData {
|
pub struct ImageData {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use crate::input_data::{decode_key_location, encode_key_location};
|
use crate::input_data::{decode_key_location, encode_key_location};
|
||||||
use dioxus_core::UiEvent;
|
use dioxus_core::Event;
|
||||||
use keyboard_types::{Code, Key, Location, Modifiers};
|
use keyboard_types::{Code, Key, Location, Modifiers};
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::fmt::{Debug, Formatter};
|
use std::fmt::{Debug, Formatter};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
pub type KeyboardEvent = UiEvent<KeyboardData>;
|
pub type KeyboardEvent = Event<KeyboardData>;
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct KeyboardData {
|
pub struct KeyboardData {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use dioxus_core::UiEvent;
|
use dioxus_core::Event;
|
||||||
|
|
||||||
pub type MediaEvent = UiEvent<MediaData>;
|
pub type MediaEvent = Event<MediaData>;
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct MediaData {}
|
pub struct MediaData {}
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ use crate::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenP
|
|||||||
use crate::input_data::{
|
use crate::input_data::{
|
||||||
decode_mouse_button_set, encode_mouse_button_set, MouseButton, MouseButtonSet,
|
decode_mouse_button_set, encode_mouse_button_set, MouseButton, MouseButtonSet,
|
||||||
};
|
};
|
||||||
use dioxus_core::UiEvent;
|
use dioxus_core::Event;
|
||||||
use keyboard_types::Modifiers;
|
use keyboard_types::Modifiers;
|
||||||
use std::fmt::{Debug, Formatter};
|
use std::fmt::{Debug, Formatter};
|
||||||
|
|
||||||
pub type MouseEvent = UiEvent<MouseData>;
|
pub type MouseEvent = Event<MouseData>;
|
||||||
|
|
||||||
/// A synthetic event that wraps a web-style [`MouseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent)
|
/// A synthetic event that wraps a web-style [`MouseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent)
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use dioxus_core::UiEvent;
|
use dioxus_core::Event;
|
||||||
|
|
||||||
pub type PointerEvent = UiEvent<PointerData>;
|
pub type PointerEvent = Event<PointerData>;
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PointerData {
|
pub struct PointerData {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use dioxus_core::UiEvent;
|
use dioxus_core::Event;
|
||||||
|
|
||||||
pub type ScrollEvent = UiEvent<ScrollData>;
|
pub type ScrollEvent = Event<ScrollData>;
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ScrollData {}
|
pub struct ScrollData {}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use dioxus_core::UiEvent;
|
use dioxus_core::Event;
|
||||||
|
|
||||||
pub type SelectionEvent = UiEvent<SelectionData>;
|
pub type SelectionEvent = Event<SelectionData>;
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct SelectionData {}
|
pub struct SelectionData {}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use dioxus_core::UiEvent;
|
use dioxus_core::Event;
|
||||||
|
|
||||||
pub type ToggleEvent = UiEvent<ToggleData>;
|
pub type ToggleEvent = Event<ToggleData>;
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ToggleData {}
|
pub struct ToggleData {}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use dioxus_core::UiEvent;
|
use dioxus_core::Event;
|
||||||
|
|
||||||
pub type TouchEvent = UiEvent<TouchData>;
|
pub type TouchEvent = Event<TouchData>;
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct TouchData {
|
pub struct TouchData {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use dioxus_core::UiEvent;
|
use dioxus_core::Event;
|
||||||
|
|
||||||
pub type TransitionEvent = UiEvent<TransitionData>;
|
pub type TransitionEvent = Event<TransitionData>;
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct TransitionData {
|
pub struct TransitionData {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
use dioxus_core::UiEvent;
|
use dioxus_core::Event;
|
||||||
use euclid::UnknownUnit;
|
use euclid::UnknownUnit;
|
||||||
use std::fmt::{Debug, Formatter};
|
use std::fmt::{Debug, Formatter};
|
||||||
|
|
||||||
use crate::geometry::{LinesVector, PagesVector, PixelsVector, WheelDelta};
|
use crate::geometry::{LinesVector, PagesVector, PixelsVector, WheelDelta};
|
||||||
|
|
||||||
pub type WheelEvent = UiEvent<WheelData>;
|
pub type WheelEvent = Event<WheelData>;
|
||||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct WheelData {
|
pub struct WheelData {
|
||||||
|
|||||||
@@ -6,9 +6,8 @@ use std::any::Any;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use dioxus_core::ElementId;
|
use dioxus_core::ElementId;
|
||||||
use dioxus_core::{EventPriority, UserEvent};
|
|
||||||
use dioxus_html::event_bubbles;
|
use dioxus_html::event_bubbles;
|
||||||
use dioxus_html::on::*;
|
use dioxus_html::events::*;
|
||||||
|
|
||||||
#[derive(serde::Serialize, serde::Deserialize)]
|
#[derive(serde::Serialize, serde::Deserialize)]
|
||||||
pub(crate) struct IpcMessage {
|
pub(crate) struct IpcMessage {
|
||||||
@@ -30,29 +29,29 @@ struct ImEvent {
|
|||||||
contents: serde_json::Value,
|
contents: serde_json::Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trigger_from_serialized(val: serde_json::Value) -> UserEvent {
|
pub fn trigger_from_serialized(val: serde_json::Value) -> () {
|
||||||
let ImEvent {
|
todo!()
|
||||||
event,
|
// let ImEvent {
|
||||||
mounted_dom_id,
|
// event,
|
||||||
contents,
|
// mounted_dom_id,
|
||||||
} = serde_json::from_value(val).unwrap();
|
// contents,
|
||||||
|
// } = serde_json::from_value(val).unwrap();
|
||||||
|
|
||||||
let mounted_dom_id = Some(mounted_dom_id);
|
// let mounted_dom_id = Some(mounted_dom_id);
|
||||||
|
|
||||||
let name = event_name_from_type(&event);
|
// let name = event_name_from_type(&event);
|
||||||
let event = make_synthetic_event(&event, contents);
|
// let event = make_synthetic_event(&event, contents);
|
||||||
|
|
||||||
UserEvent {
|
// UserEvent {
|
||||||
name,
|
// name,
|
||||||
priority: EventPriority::Low,
|
// scope_id: None,
|
||||||
scope_id: None,
|
// element: mounted_dom_id,
|
||||||
element: mounted_dom_id,
|
// data: event,
|
||||||
data: event,
|
// bubbles: event_bubbles(name),
|
||||||
bubbles: event_bubbles(name),
|
// }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_synthetic_event(name: &str, val: serde_json::Value) -> Arc<dyn Any + Send + Sync> {
|
fn make_synthetic_event(name: &str, val: serde_json::Value) -> Arc<dyn Any> {
|
||||||
match name {
|
match name {
|
||||||
"copy" | "cut" | "paste" => {
|
"copy" | "cut" | "paste" => {
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ impl<S: State> RealDom<S> {
|
|||||||
let mut nodes_updated = Vec::new();
|
let mut nodes_updated = Vec::new();
|
||||||
nodes_updated.push((RealNodeId::ElementId(ElementId(0)), NodeMask::ALL));
|
nodes_updated.push((RealNodeId::ElementId(ElementId(0)), NodeMask::ALL));
|
||||||
for mutations in mutations_vec {
|
for mutations in mutations_vec {
|
||||||
for e in mutations.edits {
|
for e in mutations.dom_edits {
|
||||||
use dioxus_core::DomEdit::*;
|
use dioxus_core::DomEdit::*;
|
||||||
match e {
|
match e {
|
||||||
AppendChildren { root, children } => {
|
AppendChildren { root, children } => {
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ impl PersistantElementIter {
|
|||||||
pub fn prune<S: State>(&mut self, mutations: &Mutations, rdom: &RealDom<S>) -> bool {
|
pub fn prune<S: State>(&mut self, mutations: &Mutations, rdom: &RealDom<S>) -> bool {
|
||||||
let mut changed = false;
|
let mut changed = false;
|
||||||
let ids_removed: Vec<_> = mutations
|
let ids_removed: Vec<_> = mutations
|
||||||
.edits
|
.dom_edits
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|e| {
|
.filter_map(|e| {
|
||||||
// nodes within templates will never be removed
|
// nodes within templates will never be removed
|
||||||
@@ -97,7 +97,7 @@ impl PersistantElementIter {
|
|||||||
for (el_id, child_idx) in self.stack.iter_mut() {
|
for (el_id, child_idx) in self.stack.iter_mut() {
|
||||||
if let NodePosition::InChild(child_idx) = child_idx {
|
if let NodePosition::InChild(child_idx) = child_idx {
|
||||||
if let NodeType::Element { children, .. } = &rdom[*el_id].node_data.node_type {
|
if let NodeType::Element { children, .. } = &rdom[*el_id].node_data.node_type {
|
||||||
for m in &mutations.edits {
|
for m in &mutations.dom_edits {
|
||||||
match m {
|
match m {
|
||||||
DomEdit::Remove { root } => {
|
DomEdit::Remove { root } => {
|
||||||
let id = rdom.resolve_maybe_id(*root);
|
let id = rdom.resolve_maybe_id(*root);
|
||||||
|
|||||||
@@ -219,7 +219,7 @@ impl ToTokens for ContentField {
|
|||||||
match self {
|
match self {
|
||||||
ContentField::ManExpr(e) => e.to_tokens(tokens),
|
ContentField::ManExpr(e) => e.to_tokens(tokens),
|
||||||
ContentField::Formatted(s) => tokens.append_all(quote! {
|
ContentField::Formatted(s) => tokens.append_all(quote! {
|
||||||
__cx.raw_text(#s).0
|
__cx.raw_text(#s)
|
||||||
}),
|
}),
|
||||||
ContentField::OnHandlerRaw(e) => tokens.append_all(quote! {
|
ContentField::OnHandlerRaw(e) => tokens.append_all(quote! {
|
||||||
__cx.event_handler(#e)
|
__cx.event_handler(#e)
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ impl ToTokens for CallBody {
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
out_tokens.append_all(quote! {
|
out_tokens.append_all(quote! {
|
||||||
::dioxus::core::LazyNodes::new( move | __cx: ::dioxus::core::NodeFactory| -> ::dioxus::core::VNode {
|
::dioxus::core::LazyNodes::new( move | __cx: &::dioxus::core::ScopeState| -> ::dioxus::core::VNode {
|
||||||
#body
|
#body
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -106,7 +106,7 @@ impl<'a> ToTokens for TemplateRenderer<'a> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let key_tokens = match key {
|
let key_tokens = match key {
|
||||||
Some(tok) => quote! { Some( __cx.raw_text_inline(#tok) ) },
|
Some(tok) => quote! { Some( __cx.raw_text(#tok) ) },
|
||||||
None => quote! { None },
|
None => quote! { None },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -131,10 +131,10 @@ impl ToTokens for BodyNode {
|
|||||||
BodyNode::Element(el) => el.to_tokens(tokens),
|
BodyNode::Element(el) => el.to_tokens(tokens),
|
||||||
BodyNode::Component(comp) => comp.to_tokens(tokens),
|
BodyNode::Component(comp) => comp.to_tokens(tokens),
|
||||||
BodyNode::Text(txt) => tokens.append_all(quote! {
|
BodyNode::Text(txt) => tokens.append_all(quote! {
|
||||||
__cx.text(#txt)
|
__cx.text_node(#txt)
|
||||||
}),
|
}),
|
||||||
BodyNode::RawExpr(exp) => tokens.append_all(quote! {
|
BodyNode::RawExpr(exp) => tokens.append_all(quote! {
|
||||||
__cx.fragment_from_iter(#exp)
|
__cx.make_node(#exp)
|
||||||
}),
|
}),
|
||||||
BodyNode::ForLoop(exp) => {
|
BodyNode::ForLoop(exp) => {
|
||||||
let ForLoop {
|
let ForLoop {
|
||||||
@@ -144,7 +144,7 @@ impl ToTokens for BodyNode {
|
|||||||
let renderer = TemplateRenderer { roots: &body };
|
let renderer = TemplateRenderer { roots: &body };
|
||||||
|
|
||||||
tokens.append_all(quote! {
|
tokens.append_all(quote! {
|
||||||
__cx.fragment_from_iter(
|
__cx.make_node(
|
||||||
(#expr).into_iter().map(|#pat| { #renderer })
|
(#expr).into_iter().map(|#pat| { #renderer })
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -152,7 +152,7 @@ impl ToTokens for BodyNode {
|
|||||||
BodyNode::IfChain(chain) => {
|
BodyNode::IfChain(chain) => {
|
||||||
if is_if_chain_terminated(chain) {
|
if is_if_chain_terminated(chain) {
|
||||||
tokens.append_all(quote! {
|
tokens.append_all(quote! {
|
||||||
__cx.fragment_from_iter(#chain)
|
__cx.make_node(#chain)
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
let ExprIf {
|
let ExprIf {
|
||||||
@@ -206,7 +206,7 @@ impl ToTokens for BodyNode {
|
|||||||
});
|
});
|
||||||
|
|
||||||
tokens.append_all(quote! {
|
tokens.append_all(quote! {
|
||||||
__cx.fragment_from_iter(#body)
|
__cx.make_node(#body)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ impl FocusState {
|
|||||||
if self.focus_iter.prune(mutations, rdom) {
|
if self.focus_iter.prune(mutations, rdom) {
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
for m in &mutations.edits {
|
for m in &mutations.dom_edits {
|
||||||
match m {
|
match m {
|
||||||
dioxus_core::DomEdit::ReplaceWith { root, .. } => remove_children(
|
dioxus_core::DomEdit::ReplaceWith { root, .. } => remove_children(
|
||||||
&mut [&mut self.last_focused_id],
|
&mut [&mut self.last_focused_id],
|
||||||
|
|||||||
+7
-28
@@ -19,36 +19,14 @@ use web_sys::{Document, Element, Event, HtmlElement};
|
|||||||
use crate::Config;
|
use crate::Config;
|
||||||
|
|
||||||
pub struct WebsysDom {
|
pub struct WebsysDom {
|
||||||
pub interpreter: Interpreter,
|
interpreter: Interpreter,
|
||||||
|
handler: Closure<dyn FnMut(&Event)>,
|
||||||
pub(crate) root: Element,
|
root: Element,
|
||||||
|
|
||||||
pub handler: Closure<dyn FnMut(&Event)>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebsysDom {
|
impl WebsysDom {
|
||||||
pub fn new(cfg: Config, event_channel: mpsc::UnboundedSender<Event>) -> Self {
|
pub fn new(cfg: Config, event_channel: mpsc::UnboundedSender<Event>) -> Self {
|
||||||
// eventually, we just want to let the interpreter do all the work of decoding events into our event type
|
// eventually, we just want to let the interpreter do all the work of decoding events into our event type
|
||||||
let callback: Box<dyn FnMut(&Event)> = Box::new(move |event: &web_sys::Event| {
|
|
||||||
_ = event_channel.unbounded_send(event.clone());
|
|
||||||
|
|
||||||
// if let Ok(synthetic_event) = decoded {
|
|
||||||
// // Try to prevent default if the attribute is set
|
|
||||||
// if let Some(node) = target.dyn_ref::<HtmlElement>() {
|
|
||||||
// if let Some(name) = node.get_attribute("dioxus-prevent-default") {
|
|
||||||
// if name == synthetic_event.name
|
|
||||||
// || name.trim_start_matches("on") == synthetic_event.name
|
|
||||||
// {
|
|
||||||
// log::trace!("Preventing default");
|
|
||||||
// event.prevent_default();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// sender_callback.as_ref()(SchedulerMsg::Event(synthetic_event))
|
|
||||||
// }
|
|
||||||
});
|
|
||||||
|
|
||||||
// a match here in order to avoid some error during runtime browser test
|
// a match here in order to avoid some error during runtime browser test
|
||||||
let document = load_document();
|
let document = load_document();
|
||||||
let root = match document.get_element_by_id(&cfg.rootname) {
|
let root = match document.get_element_by_id(&cfg.rootname) {
|
||||||
@@ -58,8 +36,10 @@ impl WebsysDom {
|
|||||||
|
|
||||||
Self {
|
Self {
|
||||||
interpreter: Interpreter::new(root.clone()),
|
interpreter: Interpreter::new(root.clone()),
|
||||||
handler: Closure::wrap(callback),
|
|
||||||
root,
|
root,
|
||||||
|
handler: Closure::wrap(Box::new(move |event: &web_sys::Event| {
|
||||||
|
let _ = event_channel.unbounded_send(event.clone());
|
||||||
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,11 +79,10 @@ impl WebsysDom {
|
|||||||
}
|
}
|
||||||
SetText { value, id } => i.SetText(id.0 as u32, value.into()),
|
SetText { value, id } => i.SetText(id.0 as u32, value.into()),
|
||||||
NewEventListener { name, scope, id } => {
|
NewEventListener { name, scope, id } => {
|
||||||
let handler: &Function = self.handler.as_ref().unchecked_ref();
|
|
||||||
self.interpreter.NewEventListener(
|
self.interpreter.NewEventListener(
|
||||||
name,
|
name,
|
||||||
id.0 as u32,
|
id.0 as u32,
|
||||||
handler,
|
self.handler.as_ref().unchecked_ref(),
|
||||||
event_bubbles(&name[2..]),
|
event_bubbles(&name[2..]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,8 +192,8 @@ pub async fn run_with_props<T: 'static>(root: fn(Scope<T>) -> Element, root_prop
|
|||||||
if should_hydrate {
|
if should_hydrate {
|
||||||
} else {
|
} else {
|
||||||
let edits = dom.rebuild();
|
let edits = dom.rebuild();
|
||||||
websys_dom.apply_edits(edits.template_mutations);
|
websys_dom.apply_edits(edits.template_edits);
|
||||||
websys_dom.apply_edits(edits.edits);
|
websys_dom.apply_edits(edits.dom_edits);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut work_loop = ric_raf::RafLoop::new();
|
let mut work_loop = ric_raf::RafLoop::new();
|
||||||
@@ -247,8 +247,8 @@ pub async fn run_with_props<T: 'static>(root: fn(Scope<T>) -> Element, root_prop
|
|||||||
|
|
||||||
log::debug!("edits {:#?}", edits);
|
log::debug!("edits {:#?}", edits);
|
||||||
|
|
||||||
websys_dom.apply_edits(edits.template_mutations);
|
websys_dom.apply_edits(edits.template_edits);
|
||||||
websys_dom.apply_edits(edits.edits);
|
websys_dom.apply_edits(edits.dom_edits);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ fn makes_tree() {
|
|||||||
let mut dom = VirtualDom::new(app);
|
let mut dom = VirtualDom::new(app);
|
||||||
let muts = dom.rebuild();
|
let muts = dom.rebuild();
|
||||||
|
|
||||||
dbg!(muts.edits);
|
dbg!(muts.dom_edits);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen_test]
|
#[wasm_bindgen_test]
|
||||||
|
|||||||
Reference in new issue
Block a user