mirror of
https://github.com/DioxusLabs/dioxus
synced 2025-02-17 06:08:26 +00:00
fix custom attribute value support
This commit is contained in:
parent
937eb1f0f0
commit
8a1c96a68c
28 changed files with 369 additions and 276 deletions
|
@ -144,8 +144,8 @@ impl Buffer {
|
||||||
let mut total = 0;
|
let mut total = 0;
|
||||||
|
|
||||||
for attr in attributes {
|
for attr in attributes {
|
||||||
if self.current_span_is_primary(attr.attr.flart()) {
|
if self.current_span_is_primary(attr.attr.start()) {
|
||||||
'line: for line in self.src[..attr.attr.flart().start().line - 1].iter().rev() {
|
'line: for line in self.src[..attr.attr.start().start().line - 1].iter().rev() {
|
||||||
match (line.trim().starts_with("//"), line.is_empty()) {
|
match (line.trim().starts_with("//"), line.is_empty()) {
|
||||||
(true, _) => return 100000,
|
(true, _) => return 100000,
|
||||||
(_, true) => continue 'line,
|
(_, true) => continue 'line,
|
||||||
|
|
|
@ -157,7 +157,7 @@ impl Buffer {
|
||||||
while let Some(attr) = attr_iter.next() {
|
while let Some(attr) = attr_iter.next() {
|
||||||
self.indent += 1;
|
self.indent += 1;
|
||||||
if !sameline {
|
if !sameline {
|
||||||
self.write_comments(attr.attr.flart())?;
|
self.write_comments(attr.attr.start())?;
|
||||||
}
|
}
|
||||||
self.indent -= 1;
|
self.indent -= 1;
|
||||||
|
|
||||||
|
|
|
@ -105,27 +105,10 @@ impl<'b> VirtualDom {
|
||||||
attribute.mounted_element.set(id);
|
attribute.mounted_element.set(id);
|
||||||
|
|
||||||
// Safety: we promise not to re-alias this text later on after committing it to the mutation
|
// Safety: we promise not to re-alias this text later on after committing it to the mutation
|
||||||
let unbounded_name = unsafe { std::mem::transmute(attribute.name) };
|
let unbounded_name: &str =
|
||||||
|
unsafe { std::mem::transmute(attribute.name) };
|
||||||
|
|
||||||
match &attribute.value {
|
match &attribute.value {
|
||||||
AttributeValue::Text(value) => {
|
|
||||||
// Safety: we promise not to re-alias this text later on after committing it to the mutation
|
|
||||||
let unbounded_value = unsafe { std::mem::transmute(*value) };
|
|
||||||
|
|
||||||
self.mutations.push(SetAttribute {
|
|
||||||
name: unbounded_name,
|
|
||||||
value: unbounded_value,
|
|
||||||
ns: attribute.namespace,
|
|
||||||
id,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
AttributeValue::Bool(value) => {
|
|
||||||
self.mutations.push(SetBoolAttribute {
|
|
||||||
name: unbounded_name,
|
|
||||||
value: *value,
|
|
||||||
id,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
AttributeValue::Listener(_) => {
|
AttributeValue::Listener(_) => {
|
||||||
self.mutations.push(NewEventListener {
|
self.mutations.push(NewEventListener {
|
||||||
// all listeners start with "on"
|
// all listeners start with "on"
|
||||||
|
@ -134,10 +117,18 @@ impl<'b> VirtualDom {
|
||||||
id,
|
id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
AttributeValue::Float(_) => todo!(),
|
_ => {
|
||||||
AttributeValue::Int(_) => todo!(),
|
// Safety: we promise not to re-alias this text later on after committing it to the mutation
|
||||||
AttributeValue::Any(_) => todo!(),
|
let unbounded_value =
|
||||||
AttributeValue::None => todo!(),
|
unsafe { std::mem::transmute(attribute.value.clone()) };
|
||||||
|
|
||||||
|
self.mutations.push(SetAttribute {
|
||||||
|
name: unbounded_name,
|
||||||
|
value: unbounded_value,
|
||||||
|
ns: attribute.namespace,
|
||||||
|
id,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only push the dynamic attributes forward if they match the current path (same element)
|
// Only push the dynamic attributes forward if they match the current path (same element)
|
||||||
|
|
|
@ -77,20 +77,14 @@ impl<'b> VirtualDom {
|
||||||
|
|
||||||
if left_attr.value != right_attr.value || left_attr.volatile {
|
if left_attr.value != right_attr.value || left_attr.volatile {
|
||||||
// todo: add more types of attribute values
|
// todo: add more types of attribute values
|
||||||
match right_attr.value {
|
let name = unsafe { std::mem::transmute(left_attr.name) };
|
||||||
AttributeValue::Text(text) => {
|
let value = unsafe { std::mem::transmute(right_attr.value.clone()) };
|
||||||
let name = unsafe { std::mem::transmute(left_attr.name) };
|
self.mutations.push(Mutation::SetAttribute {
|
||||||
let value = unsafe { std::mem::transmute(text) };
|
id: left_attr.mounted_element.get(),
|
||||||
self.mutations.push(Mutation::SetAttribute {
|
ns: right_attr.namespace,
|
||||||
id: left_attr.mounted_element.get(),
|
name,
|
||||||
ns: right_attr.namespace,
|
value,
|
||||||
name,
|
});
|
||||||
value,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// todo: more types of attribute values
|
|
||||||
_ => todo!("other attribute types"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,10 +70,10 @@ pub(crate) mod innerlude {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use crate::innerlude::{
|
pub use crate::innerlude::{
|
||||||
fc_to_builder, Attribute, AttributeValue, Component, DynamicNode, Element, ElementId, Event,
|
fc_to_builder, AnyValueBox, Attribute, AttributeValue, Component, DynamicNode, Element,
|
||||||
Fragment, IntoDynNode, LazyNodes, Mutation, Mutations, Properties, RenderReturn, Scope,
|
ElementId, Event, Fragment, IntoAttributeValue, IntoDynNode, LazyNodes, Mutation, Mutations,
|
||||||
ScopeId, ScopeState, Scoped, SuspenseContext, TaskId, Template, TemplateAttribute,
|
Properties, RenderReturn, Scope, ScopeId, ScopeState, Scoped, SuspenseContext, TaskId,
|
||||||
TemplateNode, VComponent, VNode, VText, VirtualDom,
|
Template, TemplateAttribute, TemplateNode, 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
|
||||||
|
@ -81,9 +81,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, Event, EventHandler, Fragment, LazyNodes, Properties, Scope,
|
fc_to_builder, Element, Event, EventHandler, Fragment, IntoAttributeValue, LazyNodes,
|
||||||
ScopeId, ScopeState, Scoped, TaskId, Template, TemplateAttribute, TemplateNode, VNode,
|
Properties, Scope, ScopeId, ScopeState, Scoped, TaskId, Template, TemplateAttribute,
|
||||||
VirtualDom,
|
TemplateNode, VNode, VirtualDom,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use fxhash::FxHashSet;
|
use fxhash::FxHashSet;
|
||||||
|
|
||||||
use crate::{arena::ElementId, ScopeId, Template};
|
use crate::{arena::ElementId, AttributeValue, ScopeId, Template};
|
||||||
|
|
||||||
/// A container for all the relevant steps to modify the Real DOM
|
/// A container for all the relevant steps to modify the Real DOM
|
||||||
///
|
///
|
||||||
|
@ -48,7 +48,7 @@ impl<'a> Mutations<'a> {
|
||||||
|
|
||||||
/// Push a new mutation into the dom_edits list
|
/// Push a new mutation into the dom_edits list
|
||||||
pub(crate) fn push(&mut self, mutation: Mutation<'static>) {
|
pub(crate) fn push(&mut self, mutation: Mutation<'static>) {
|
||||||
self.edits.push(mutation)
|
unsafe { self.edits.push(std::mem::transmute(mutation)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ impl<'a> Mutations<'a> {
|
||||||
derive(serde::Serialize, serde::Deserialize),
|
derive(serde::Serialize, serde::Deserialize),
|
||||||
serde(tag = "type")
|
serde(tag = "type")
|
||||||
)]
|
)]
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum Mutation<'a> {
|
pub enum Mutation<'a> {
|
||||||
/// Add these m children to the target element
|
/// Add these m children to the target element
|
||||||
AppendChildren {
|
AppendChildren {
|
||||||
|
@ -193,8 +193,9 @@ pub enum Mutation<'a> {
|
||||||
SetAttribute {
|
SetAttribute {
|
||||||
/// The name of the attribute to set.
|
/// The name of the attribute to set.
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
|
|
||||||
/// The value of the attribute.
|
/// The value of the attribute.
|
||||||
value: &'a str,
|
value: AttributeValue<'a>,
|
||||||
|
|
||||||
/// The ID of the node to set the attribute of.
|
/// The ID of the node to set the attribute of.
|
||||||
id: ElementId,
|
id: ElementId,
|
||||||
|
@ -204,18 +205,6 @@ pub enum Mutation<'a> {
|
||||||
ns: Option<&'a str>,
|
ns: Option<&'a str>,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Set the value of a node's attribute.
|
|
||||||
SetBoolAttribute {
|
|
||||||
/// The name of the attribute to set.
|
|
||||||
name: &'a str,
|
|
||||||
|
|
||||||
/// The value of the attribute.
|
|
||||||
value: bool,
|
|
||||||
|
|
||||||
/// The ID of the node to set the attribute of.
|
|
||||||
id: ElementId,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Set the textcontent of a node.
|
/// Set the textcontent of a node.
|
||||||
SetText {
|
SetText {
|
||||||
/// The textcontent of the node
|
/// The textcontent of the node
|
||||||
|
|
|
@ -8,6 +8,7 @@ use std::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
fmt::Arguments,
|
fmt::Arguments,
|
||||||
future::Future,
|
future::Future,
|
||||||
|
rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type TemplateId = &'static str;
|
pub type TemplateId = &'static str;
|
||||||
|
@ -88,7 +89,7 @@ impl<'a> VNode<'a> {
|
||||||
pub(crate) fn clear_listeners(&self) {
|
pub(crate) fn clear_listeners(&self) {
|
||||||
for attr in self.dynamic_attrs {
|
for attr in self.dynamic_attrs {
|
||||||
if let AttributeValue::Listener(l) = &attr.value {
|
if let AttributeValue::Listener(l) = &attr.value {
|
||||||
l.borrow_mut().take();
|
l.0.borrow_mut().take();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -317,6 +318,9 @@ pub struct Attribute<'a> {
|
||||||
///
|
///
|
||||||
/// These are built-in to be faster during the diffing process. To use a custom value, use the [`AttributeValue::Any`]
|
/// These are built-in to be faster during the diffing process. To use a custom value, use the [`AttributeValue::Any`]
|
||||||
/// variant.
|
/// variant.
|
||||||
|
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[cfg_attr(feature = "serialize", serde(untagged))]
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum AttributeValue<'a> {
|
pub enum AttributeValue<'a> {
|
||||||
/// Text attribute
|
/// Text attribute
|
||||||
Text(&'a str),
|
Text(&'a str),
|
||||||
|
@ -331,16 +335,67 @@ pub enum AttributeValue<'a> {
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
|
|
||||||
/// A listener, like "onclick"
|
/// A listener, like "onclick"
|
||||||
Listener(RefCell<Option<ListenerCb<'a>>>),
|
Listener(ListenerCb<'a>),
|
||||||
|
|
||||||
/// An arbitrary value that implements PartialEq and is static
|
/// An arbitrary value that implements PartialEq and is static
|
||||||
Any(BumpBox<'a, dyn AnyValue>),
|
Any(AnyValueBox),
|
||||||
|
|
||||||
/// A "none" value, resulting in the removal of an attribute from the dom
|
/// A "none" value, resulting in the removal of an attribute from the dom
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
type ListenerCb<'a> = BumpBox<'a, dyn FnMut(Event<dyn Any>) + 'a>;
|
pub type ListenerCbInner<'a> = RefCell<Option<BumpBox<'a, dyn FnMut(Event<dyn Any>) + 'a>>>;
|
||||||
|
pub struct ListenerCb<'a>(pub ListenerCbInner<'a>);
|
||||||
|
|
||||||
|
impl Clone for ListenerCb<'_> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
panic!("ListenerCb cannot be cloned")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serialize")]
|
||||||
|
impl<'a> serde::Serialize for ListenerCb<'a> {
|
||||||
|
fn serialize<S>(&self, _: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
panic!("ListenerCb cannot be serialized")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serialize")]
|
||||||
|
impl<'de, 'a> serde::Deserialize<'de> for ListenerCb<'a> {
|
||||||
|
fn deserialize<D>(_: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
panic!("ListenerCb cannot be deserialized")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A boxed value that implements PartialEq and Any
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct AnyValueBox(pub Rc<dyn AnyValue>);
|
||||||
|
|
||||||
|
#[cfg(feature = "serialize")]
|
||||||
|
impl serde::Serialize for AnyValueBox {
|
||||||
|
fn serialize<S>(&self, _: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
panic!("AnyValueBox cannot be serialized")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serialize")]
|
||||||
|
impl<'de> serde::Deserialize<'de> for AnyValueBox {
|
||||||
|
fn deserialize<D>(_: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
panic!("AnyValueBox cannot be deserialized")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> std::fmt::Debug for AttributeValue<'a> {
|
impl<'a> std::fmt::Debug for AttributeValue<'a> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
@ -364,8 +419,8 @@ impl<'a> PartialEq for AttributeValue<'a> {
|
||||||
(Self::Int(l0), Self::Int(r0)) => l0 == r0,
|
(Self::Int(l0), Self::Int(r0)) => l0 == r0,
|
||||||
(Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
|
(Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
|
||||||
(Self::Listener(_), Self::Listener(_)) => true,
|
(Self::Listener(_), Self::Listener(_)) => true,
|
||||||
(Self::Any(l0), Self::Any(r0)) => l0.any_cmp(r0.as_ref()),
|
(Self::Any(l0), Self::Any(r0)) => l0.0.any_cmp(r0.0.as_ref()),
|
||||||
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -559,21 +614,25 @@ impl<'a> IntoAttributeValue<'a> for &'a str {
|
||||||
AttributeValue::Text(self)
|
AttributeValue::Text(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IntoAttributeValue<'a> for f64 {
|
impl<'a> IntoAttributeValue<'a> for f64 {
|
||||||
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
|
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
|
||||||
AttributeValue::Float(self)
|
AttributeValue::Float(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IntoAttributeValue<'a> for i64 {
|
impl<'a> IntoAttributeValue<'a> for i64 {
|
||||||
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
|
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
|
||||||
AttributeValue::Int(self)
|
AttributeValue::Int(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IntoAttributeValue<'a> for bool {
|
impl<'a> IntoAttributeValue<'a> for bool {
|
||||||
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
|
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
|
||||||
AttributeValue::Bool(self)
|
AttributeValue::Bool(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IntoAttributeValue<'a> for Arguments<'_> {
|
impl<'a> IntoAttributeValue<'a> for Arguments<'_> {
|
||||||
fn into_value(self, bump: &'a Bump) -> AttributeValue<'a> {
|
fn into_value(self, bump: &'a Bump) -> AttributeValue<'a> {
|
||||||
use bumpalo::core_alloc::fmt::Write;
|
use bumpalo::core_alloc::fmt::Write;
|
||||||
|
@ -582,3 +641,9 @@ impl<'a> IntoAttributeValue<'a> for Arguments<'_> {
|
||||||
AttributeValue::Text(str_buf.into_bump_str())
|
AttributeValue::Text(str_buf.into_bump_str())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoAttributeValue<'a> for AnyValueBox {
|
||||||
|
fn into_value(self, _: &'a Bump) -> AttributeValue<'a> {
|
||||||
|
AttributeValue::Any(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::{
|
||||||
arena::ElementId,
|
arena::ElementId,
|
||||||
bump_frame::BumpFrame,
|
bump_frame::BumpFrame,
|
||||||
innerlude::{DynamicNode, EventHandler, VComponent, VText},
|
innerlude::{DynamicNode, EventHandler, VComponent, VText},
|
||||||
innerlude::{Scheduler, SchedulerMsg},
|
innerlude::{ListenerCb, Scheduler, SchedulerMsg},
|
||||||
lazynodes::LazyNodes,
|
lazynodes::LazyNodes,
|
||||||
nodes::{ComponentReturn, IntoAttributeValue, IntoDynNode, RenderReturn},
|
nodes::{ComponentReturn, IntoAttributeValue, IntoDynNode, RenderReturn},
|
||||||
Attribute, AttributeValue, Element, Event, Properties, TaskId,
|
Attribute, AttributeValue, Element, Event, Properties, TaskId,
|
||||||
|
@ -483,7 +483,7 @@ impl<'src> ScopeState {
|
||||||
}))
|
}))
|
||||||
};
|
};
|
||||||
|
|
||||||
AttributeValue::Listener(RefCell::new(Some(boxed)))
|
AttributeValue::Listener(ListenerCb(RefCell::new(Some(boxed))))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Store a value between renders. The foundational hook for all other hooks.
|
/// Store a value between renders. The foundational hook for all other hooks.
|
||||||
|
|
|
@ -386,7 +386,7 @@ impl VirtualDom {
|
||||||
// We check the bubble state between each call to see if the event has been stopped from bubbling
|
// We check the bubble state between each call to see if the event has been stopped from bubbling
|
||||||
for listener in listeners.drain(..).rev() {
|
for listener in listeners.drain(..).rev() {
|
||||||
if let AttributeValue::Listener(listener) = listener {
|
if let AttributeValue::Listener(listener) = listener {
|
||||||
if let Some(cb) = listener.borrow_mut().as_deref_mut() {
|
if let Some(cb) = listener.0.borrow_mut().as_deref_mut() {
|
||||||
cb(uievent.clone());
|
cb(uievent.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -493,7 +493,7 @@ impl VirtualDom {
|
||||||
RenderReturn::Async(_) => unreachable!("Root scope cannot be an async component"),
|
RenderReturn::Async(_) => unreachable!("Root scope cannot be an async component"),
|
||||||
}
|
}
|
||||||
|
|
||||||
self.finalize()
|
unsafe { std::mem::transmute(self.finalize()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Render whatever the VirtualDom has ready as fast as possible without requiring an executor to progress
|
/// Render whatever the VirtualDom has ready as fast as possible without requiring an executor to progress
|
||||||
|
@ -591,7 +591,7 @@ impl VirtualDom {
|
||||||
|
|
||||||
// If there's no pending suspense, then we have no reason to wait for anything
|
// If there's no pending suspense, then we have no reason to wait for anything
|
||||||
if self.scheduler.leaves.borrow().is_empty() {
|
if self.scheduler.leaves.borrow().is_empty() {
|
||||||
return self.finalize();
|
return unsafe { std::mem::transmute(self.finalize()) };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Poll the suspense leaves in the meantime
|
// Poll the suspense leaves in the meantime
|
||||||
|
@ -605,13 +605,13 @@ impl VirtualDom {
|
||||||
if let Either::Left((_, _)) = select(&mut deadline, pinned).await {
|
if let Either::Left((_, _)) = select(&mut deadline, pinned).await {
|
||||||
// release the borrowed
|
// release the borrowed
|
||||||
drop(work);
|
drop(work);
|
||||||
return self.finalize();
|
return unsafe { std::mem::transmute(self.finalize()) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Swap the current mutations with a new
|
/// Swap the current mutations with a new
|
||||||
fn finalize(&mut self) -> Mutations {
|
fn finalize(&mut self) -> Mutations<'static> {
|
||||||
// todo: make this a routine
|
// todo: make this a routine
|
||||||
let mut out = Mutations::default();
|
let mut out = Mutations::default();
|
||||||
std::mem::swap(&mut self.mutations, &mut out);
|
std::mem::swap(&mut self.mutations, &mut out);
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
//!
|
//!
|
||||||
//! This tests to ensure we clean it up
|
//! This tests to ensure we clean it up
|
||||||
|
|
||||||
|
use bumpalo::Bump;
|
||||||
use dioxus::core::{ElementId, Mutation::*};
|
use dioxus::core::{ElementId, Mutation::*};
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
@ -22,6 +23,8 @@ fn attrs_cycle() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let bump = Bump::new();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dom.rebuild().santize().edits,
|
dom.rebuild().santize().edits,
|
||||||
[
|
[
|
||||||
|
@ -36,8 +39,13 @@ fn attrs_cycle() {
|
||||||
[
|
[
|
||||||
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,) },
|
||||||
SetAttribute { name: "class", value: "1", id: ElementId(3,), ns: None },
|
SetAttribute {
|
||||||
SetAttribute { name: "id", value: "1", id: ElementId(3,), ns: None },
|
name: "class",
|
||||||
|
value: "1".into_value(&bump),
|
||||||
|
id: ElementId(3,),
|
||||||
|
ns: None
|
||||||
|
},
|
||||||
|
SetAttribute { name: "id", value: "1".into_value(&bump), id: ElementId(3,), ns: None },
|
||||||
ReplaceWith { id: ElementId(1,), m: 1 },
|
ReplaceWith { id: ElementId(1,), m: 1 },
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -57,8 +65,13 @@ fn attrs_cycle() {
|
||||||
[
|
[
|
||||||
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) },
|
||||||
SetAttribute { name: "class", value: "3", id: ElementId(1), ns: None },
|
SetAttribute {
|
||||||
SetAttribute { name: "id", value: "3", id: ElementId(1), ns: None },
|
name: "class",
|
||||||
|
value: "3".into_value(&bump),
|
||||||
|
id: ElementId(1),
|
||||||
|
ns: None
|
||||||
|
},
|
||||||
|
SetAttribute { name: "id", value: "3".into_value(&bump), id: ElementId(1), ns: None },
|
||||||
ReplaceWith { id: ElementId(3), m: 1 }
|
ReplaceWith { id: ElementId(3), m: 1 }
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,15 +1,22 @@
|
||||||
|
use bumpalo::Bump;
|
||||||
use dioxus::core::{ElementId, Mutation::*};
|
use dioxus::core::{ElementId, Mutation::*};
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
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 })));
|
||||||
|
let bump = Bump::new();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
app.rebuild().santize().edits,
|
app.rebuild().santize().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,) },
|
SetAttribute {
|
||||||
|
name: "hidden",
|
||||||
|
value: false.into_value(&bump),
|
||||||
|
id: ElementId(1,),
|
||||||
|
ns: None
|
||||||
|
},
|
||||||
AppendChildren { m: 1, id: ElementId(0) },
|
AppendChildren { m: 1, id: ElementId(0) },
|
||||||
]
|
]
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ fn test_borrowed_state() {
|
||||||
ReplacePlaceholder { path: &[0,], m: 1 },
|
ReplacePlaceholder { path: &[0,], m: 1 },
|
||||||
AppendChildren { m: 1, id: ElementId(0) },
|
AppendChildren { m: 1, id: ElementId(0) },
|
||||||
]
|
]
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Parent(cx: Scope) -> Element {
|
fn Parent(cx: Scope) -> Element {
|
||||||
|
|
|
@ -20,7 +20,9 @@ fn app(cx: Scope) -> Element {
|
||||||
fn bubbles_error() {
|
fn bubbles_error() {
|
||||||
let mut dom = VirtualDom::new(app);
|
let mut dom = VirtualDom::new(app);
|
||||||
|
|
||||||
let _edits = dom.rebuild().santize();
|
{
|
||||||
|
let _edits = dom.rebuild().santize();
|
||||||
|
}
|
||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
|
|
||||||
|
|
|
@ -12,14 +12,16 @@ fn cycling_elements() {
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let edits = dom.rebuild().santize();
|
{
|
||||||
assert_eq!(
|
let edits = dom.rebuild().santize();
|
||||||
edits.edits,
|
assert_eq!(
|
||||||
[
|
edits.edits,
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
[
|
||||||
AppendChildren { m: 1, id: ElementId(0) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
||||||
]
|
AppendChildren { m: 1, id: ElementId(0) },
|
||||||
);
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
|
@ -59,19 +59,21 @@ fn component_swap() {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut dom = VirtualDom::new(app);
|
let mut dom = VirtualDom::new(app);
|
||||||
let edits = dom.rebuild().santize();
|
{
|
||||||
assert_eq!(
|
let edits = dom.rebuild().santize();
|
||||||
edits.edits,
|
assert_eq!(
|
||||||
[
|
edits.edits,
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(2) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(1) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(3) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(2) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(4) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(3) },
|
||||||
ReplacePlaceholder { path: &[1], m: 3 },
|
LoadTemplate { name: "template", index: 0, id: ElementId(4) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(5) },
|
ReplacePlaceholder { path: &[1], m: 3 },
|
||||||
AppendChildren { m: 2, id: ElementId(0) }
|
LoadTemplate { name: "template", index: 0, id: ElementId(5) },
|
||||||
]
|
AppendChildren { m: 2, id: ElementId(0) }
|
||||||
);
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
|
@ -20,22 +20,24 @@ fn keyed_diffing_out_of_order() {
|
||||||
cx.render(rsx!(order.iter().map(|i| rsx!(div { key: "{i}" }))))
|
cx.render(rsx!(order.iter().map(|i| rsx!(div { key: "{i}" }))))
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(
|
{
|
||||||
dom.rebuild().santize().edits,
|
assert_eq!(
|
||||||
[
|
dom.rebuild().santize().edits,
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
[
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(3,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(4,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(3,) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(5,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(4,) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(6,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(5,) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(7,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(6,) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(8,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(7,) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(9,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(8,) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(10,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(9,) },
|
||||||
AppendChildren { m: 10, id: ElementId(0) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(10,) },
|
||||||
]
|
AppendChildren { m: 10, id: ElementId(0) },
|
||||||
);
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -44,7 +46,7 @@ fn keyed_diffing_out_of_order() {
|
||||||
PushRoot { id: ElementId(7,) },
|
PushRoot { id: ElementId(7,) },
|
||||||
InsertBefore { id: ElementId(5,), m: 1 },
|
InsertBefore { id: ElementId(5,), m: 1 },
|
||||||
]
|
]
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Should result in moves only
|
/// Should result in moves only
|
||||||
|
@ -70,7 +72,7 @@ fn keyed_diffing_out_of_order_adds() {
|
||||||
PushRoot { id: ElementId(4,) },
|
PushRoot { id: ElementId(4,) },
|
||||||
InsertBefore { id: ElementId(1,), m: 2 },
|
InsertBefore { id: ElementId(1,), m: 2 },
|
||||||
]
|
]
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Should result in moves only
|
/// Should result in moves only
|
||||||
|
|
|
@ -314,66 +314,76 @@ fn remove_many() {
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let edits = dom.rebuild().santize();
|
{
|
||||||
assert!(edits.templates.is_empty());
|
let edits = dom.rebuild().santize();
|
||||||
assert_eq!(
|
assert!(edits.templates.is_empty());
|
||||||
edits.edits,
|
assert_eq!(
|
||||||
[
|
edits.edits,
|
||||||
CreatePlaceholder { id: ElementId(1,) },
|
[
|
||||||
AppendChildren { id: ElementId(0), m: 1 },
|
CreatePlaceholder { id: ElementId(1,) },
|
||||||
]
|
AppendChildren { id: ElementId(0), m: 1 },
|
||||||
);
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
{
|
||||||
let edits = dom.render_immediate().santize();
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
let edits = dom.render_immediate().santize();
|
||||||
edits.edits,
|
assert_eq!(
|
||||||
[
|
edits.edits,
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
[
|
||||||
HydrateText { path: &[0,], value: "hello 0", id: ElementId(3,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
||||||
ReplaceWith { id: ElementId(1,), m: 1 },
|
HydrateText { path: &[0,], value: "hello 0", id: ElementId(3,) },
|
||||||
]
|
ReplaceWith { id: ElementId(1,), m: 1 },
|
||||||
);
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
{
|
||||||
let edits = dom.render_immediate().santize();
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
let edits = dom.render_immediate().santize();
|
||||||
edits.edits,
|
assert_eq!(
|
||||||
[
|
edits.edits,
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
[
|
||||||
HydrateText { path: &[0,], value: "hello 1", id: ElementId(4,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(1,) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(5,) },
|
HydrateText { path: &[0,], value: "hello 1", id: ElementId(4,) },
|
||||||
HydrateText { path: &[0,], value: "hello 2", id: ElementId(6,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(5,) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(7,) },
|
HydrateText { path: &[0,], value: "hello 2", id: ElementId(6,) },
|
||||||
HydrateText { path: &[0,], value: "hello 3", id: ElementId(8,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(7,) },
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(9,) },
|
HydrateText { path: &[0,], value: "hello 3", id: ElementId(8,) },
|
||||||
HydrateText { path: &[0,], value: "hello 4", id: ElementId(10,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(9,) },
|
||||||
InsertAfter { id: ElementId(2,), m: 4 },
|
HydrateText { path: &[0,], value: "hello 4", id: ElementId(10,) },
|
||||||
]
|
InsertAfter { id: ElementId(2,), m: 4 },
|
||||||
);
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
{
|
||||||
let edits = dom.render_immediate().santize();
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
let edits = dom.render_immediate().santize();
|
||||||
edits.edits,
|
assert_eq!(
|
||||||
[
|
edits.edits,
|
||||||
Remove { id: ElementId(9,) },
|
[
|
||||||
Remove { id: ElementId(7,) },
|
Remove { id: ElementId(9,) },
|
||||||
Remove { id: ElementId(5,) },
|
Remove { id: ElementId(7,) },
|
||||||
Remove { id: ElementId(1,) },
|
Remove { id: ElementId(5,) },
|
||||||
CreatePlaceholder { id: ElementId(3,) },
|
Remove { id: ElementId(1,) },
|
||||||
ReplaceWith { id: ElementId(2,), m: 1 },
|
CreatePlaceholder { id: ElementId(3,) },
|
||||||
]
|
ReplaceWith { id: ElementId(2,), m: 1 },
|
||||||
);
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
dom.mark_dirty(ScopeId(0));
|
{
|
||||||
let edits = dom.render_immediate().santize();
|
dom.mark_dirty(ScopeId(0));
|
||||||
assert_eq!(
|
let edits = dom.render_immediate().santize();
|
||||||
edits.edits,
|
assert_eq!(
|
||||||
[
|
edits.edits,
|
||||||
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
[
|
||||||
HydrateText { path: &[0,], value: "hello 0", id: ElementId(1,) },
|
LoadTemplate { name: "template", index: 0, id: ElementId(2,) },
|
||||||
ReplaceWith { id: ElementId(3,), m: 1 },
|
HydrateText { path: &[0,], value: "hello 0", id: ElementId(1,) },
|
||||||
]
|
ReplaceWith { id: ElementId(3,), m: 1 },
|
||||||
)
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use bumpalo::Bump;
|
||||||
use dioxus::core::{ElementId, Mutation};
|
use dioxus::core::{ElementId, Mutation};
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
@ -26,17 +27,22 @@ fn basic_syntax_is_a_template(cx: Scope) -> Element {
|
||||||
#[test]
|
#[test]
|
||||||
fn dual_stream() {
|
fn dual_stream() {
|
||||||
let mut dom = VirtualDom::new(basic_syntax_is_a_template);
|
let mut dom = VirtualDom::new(basic_syntax_is_a_template);
|
||||||
|
let bump = Bump::new();
|
||||||
let edits = dom.rebuild().santize();
|
let edits = dom.rebuild().santize();
|
||||||
|
|
||||||
use Mutation::*;
|
use Mutation::*;
|
||||||
assert_eq!(
|
assert_eq!(edits.edits, {
|
||||||
edits.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".into_value(&bump),
|
||||||
|
id: ElementId(1),
|
||||||
|
ns: None,
|
||||||
|
},
|
||||||
NewEventListener { name: "click", scope: ScopeId(0), id: ElementId(1) },
|
NewEventListener { name: "click", scope: ScopeId(0), id: ElementId(1) },
|
||||||
HydrateText { path: &[0, 0], value: "123", id: ElementId(2) },
|
HydrateText { path: &[0, 0], value: "123", id: ElementId(2) },
|
||||||
AppendChildren { id: ElementId(0), m: 1 }
|
AppendChildren { id: ElementId(0), m: 1 },
|
||||||
],
|
]
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,32 +9,34 @@ use std::time::Duration;
|
||||||
async fn it_works() {
|
async fn it_works() {
|
||||||
let mut dom = VirtualDom::new(app);
|
let mut dom = VirtualDom::new(app);
|
||||||
|
|
||||||
let mutations = dom.rebuild().santize();
|
{
|
||||||
|
let mutations = dom.rebuild().santize();
|
||||||
|
|
||||||
// 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
|
||||||
// note: we dont test template edits anymore
|
// note: we dont test template edits anymore
|
||||||
// assert_eq!(
|
// assert_eq!(
|
||||||
// mutations.templates,
|
// mutations.templates,
|
||||||
// [
|
// [
|
||||||
// CreateElement { name: "div" },
|
// CreateElement { name: "div" },
|
||||||
// CreateStaticText { value: "Waiting for child..." },
|
// CreateStaticText { value: "Waiting for child..." },
|
||||||
// CreateStaticPlaceholder,
|
// CreateStaticPlaceholder,
|
||||||
// AppendChildren { m: 2 },
|
// AppendChildren { m: 2 },
|
||||||
// SaveTemplate { name: "template", m: 1 }
|
// SaveTemplate { name: "template", m: 1 }
|
||||||
// ]
|
// ]
|
||||||
// );
|
// );
|
||||||
|
|
||||||
// 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.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?
|
||||||
// can we even?
|
// can we even?
|
||||||
AssignId { path: &[1], id: ElementId(3) },
|
AssignId { path: &[1], id: ElementId(3) },
|
||||||
AppendChildren { m: 1, id: ElementId(0) },
|
AppendChildren { m: 1, id: ElementId(0) },
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// wait just a moment, not enough time for the boundary to resolve
|
// wait just a moment, not enough time for the boundary to resolve
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ fn create_rows(c: &mut Criterion) {
|
||||||
|
|
||||||
c.bench_function("create rows", |b| {
|
c.bench_function("create rows", |b| {
|
||||||
let mut dom = VirtualDom::new(app);
|
let mut dom = VirtualDom::new(app);
|
||||||
dom.rebuild();
|
let _ = dom.rebuild();
|
||||||
|
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
let g = dom.rebuild();
|
let g = dom.rebuild();
|
||||||
|
|
|
@ -18,10 +18,9 @@ proc-macro = true
|
||||||
syn = { version = "1.0.11", features = ["extra-traits"] }
|
syn = { version = "1.0.11", features = ["extra-traits"] }
|
||||||
quote = "1.0"
|
quote = "1.0"
|
||||||
dioxus-native-core = { path = "../native-core", version = "^0.2.0" }
|
dioxus-native-core = { path = "../native-core", version = "^0.2.0" }
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
dioxus = { path = "../dioxus" }
|
dioxus = { path = "../dioxus" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
smallvec = "1.6"
|
smallvec = "1.6"
|
||||||
rustc-hash = "1.1.0"
|
rustc-hash = "1.1.0"
|
||||||
anymap = "0.12.1"
|
anymap = "0.12.1"
|
||||||
|
|
|
@ -306,8 +306,7 @@ impl Member {
|
||||||
+ field.ty.to_token_stream().to_string().as_str())
|
+ field.ty.to_token_stream().to_string().as_str())
|
||||||
.as_str(),
|
.as_str(),
|
||||||
Span::call_site(),
|
Span::call_site(),
|
||||||
)
|
),
|
||||||
.into(),
|
|
||||||
ident: field.ident.as_ref()?.clone(),
|
ident: field.ident.as_ref()?.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::{state::State, tree::NodeId};
|
use crate::{state::State, tree::NodeId};
|
||||||
use dioxus_core::ElementId;
|
use dioxus_core::{AnyValueBox, AttributeValue, ElementId};
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
/// The node is stored client side and stores only basic data about the node.
|
/// The node is stored client side and stores only basic data about the node.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -72,15 +73,43 @@ pub struct OwnedAttributeView<'a> {
|
||||||
pub value: &'a OwnedAttributeValue,
|
pub value: &'a OwnedAttributeValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone)]
|
||||||
pub enum OwnedAttributeValue {
|
pub enum OwnedAttributeValue {
|
||||||
Text(String),
|
Text(String),
|
||||||
Float(f32),
|
Float(f64),
|
||||||
Int(i32),
|
Int(i64),
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
|
Any(AnyValueBox),
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Debug for OwnedAttributeValue {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Text(arg0) => f.debug_tuple("Text").field(arg0).finish(),
|
||||||
|
Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
|
||||||
|
Self::Int(arg0) => f.debug_tuple("Int").field(arg0).finish(),
|
||||||
|
Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
|
||||||
|
Self::Any(_) => f.debug_tuple("Any").finish(),
|
||||||
|
Self::None => write!(f, "None"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<AttributeValue<'_>> for OwnedAttributeValue {
|
||||||
|
fn from(value: AttributeValue<'_>) -> Self {
|
||||||
|
match value {
|
||||||
|
AttributeValue::Text(text) => Self::Text(text.to_string()),
|
||||||
|
AttributeValue::Float(float) => Self::Float(float),
|
||||||
|
AttributeValue::Int(int) => Self::Int(int),
|
||||||
|
AttributeValue::Bool(bool) => Self::Bool(bool),
|
||||||
|
AttributeValue::Any(any) => Self::Any(any),
|
||||||
|
AttributeValue::None => Self::None,
|
||||||
|
_ => Self::None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl OwnedAttributeValue {
|
impl OwnedAttributeValue {
|
||||||
pub fn as_text(&self) -> Option<&str> {
|
pub fn as_text(&self) -> Option<&str> {
|
||||||
match self {
|
match self {
|
||||||
|
@ -89,14 +118,14 @@ impl OwnedAttributeValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_float(&self) -> Option<f32> {
|
pub fn as_float(&self) -> Option<f64> {
|
||||||
match self {
|
match self {
|
||||||
OwnedAttributeValue::Float(float) => Some(*float),
|
OwnedAttributeValue::Float(float) => Some(*float),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_int(&self) -> Option<i32> {
|
pub fn as_int(&self) -> Option<i64> {
|
||||||
match self {
|
match self {
|
||||||
OwnedAttributeValue::Int(int) => Some(*int),
|
OwnedAttributeValue::Int(int) => Some(*int),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
|
@ -102,11 +102,7 @@ impl<S: State> RealDom<S> {
|
||||||
self.tree.add_child(node_id, child_id);
|
self.tree.add_child(node_id, child_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_template_node(
|
fn create_template_node(&mut self, node: &TemplateNode) -> RealNodeId {
|
||||||
&mut self,
|
|
||||||
node: &TemplateNode,
|
|
||||||
mutations_vec: &mut FxHashMap<RealNodeId, NodeMask>,
|
|
||||||
) -> RealNodeId {
|
|
||||||
match node {
|
match node {
|
||||||
TemplateNode::Element {
|
TemplateNode::Element {
|
||||||
tag,
|
tag,
|
||||||
|
@ -139,27 +135,18 @@ impl<S: State> RealDom<S> {
|
||||||
});
|
});
|
||||||
let node_id = self.create_node(node);
|
let node_id = self.create_node(node);
|
||||||
for child in *children {
|
for child in *children {
|
||||||
let child_id = self.create_template_node(child, mutations_vec);
|
let child_id = self.create_template_node(child);
|
||||||
self.add_child(node_id, child_id);
|
self.add_child(node_id, child_id);
|
||||||
}
|
}
|
||||||
node_id
|
node_id
|
||||||
}
|
}
|
||||||
TemplateNode::Text { text } => {
|
TemplateNode::Text { text } => self.create_node(Node::new(NodeType::Text {
|
||||||
let node_id = self.create_node(Node::new(NodeType::Text {
|
text: text.to_string(),
|
||||||
text: text.to_string(),
|
})),
|
||||||
}));
|
TemplateNode::Dynamic { .. } => self.create_node(Node::new(NodeType::Placeholder)),
|
||||||
node_id
|
TemplateNode::DynamicText { .. } => self.create_node(Node::new(NodeType::Text {
|
||||||
}
|
text: String::new(),
|
||||||
TemplateNode::Dynamic { .. } => {
|
})),
|
||||||
let node_id = self.create_node(Node::new(NodeType::Placeholder));
|
|
||||||
node_id
|
|
||||||
}
|
|
||||||
TemplateNode::DynamicText { .. } => {
|
|
||||||
let node_id = self.create_node(Node::new(NodeType::Text {
|
|
||||||
text: String::new(),
|
|
||||||
}));
|
|
||||||
node_id
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,7 +159,7 @@ impl<S: State> RealDom<S> {
|
||||||
for template in mutations.templates {
|
for template in mutations.templates {
|
||||||
let mut template_root_ids = Vec::new();
|
let mut template_root_ids = Vec::new();
|
||||||
for root in template.roots {
|
for root in template.roots {
|
||||||
let id = self.create_template_node(root, &mut nodes_updated);
|
let id = self.create_template_node(root);
|
||||||
template_root_ids.push(id);
|
template_root_ids.push(id);
|
||||||
}
|
}
|
||||||
self.templates
|
self.templates
|
||||||
|
@ -283,26 +270,7 @@ impl<S: State> RealDom<S> {
|
||||||
namespace: ns.map(|s| s.to_string()),
|
namespace: ns.map(|s| s.to_string()),
|
||||||
volatile: false,
|
volatile: false,
|
||||||
},
|
},
|
||||||
crate::node::OwnedAttributeValue::Text(value.to_string()),
|
OwnedAttributeValue::from(value),
|
||||||
);
|
|
||||||
mark_dirty(
|
|
||||||
node_id,
|
|
||||||
NodeMask::new_with_attrs(AttributeMask::single(name)),
|
|
||||||
&mut nodes_updated,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SetBoolAttribute { name, value, id } => {
|
|
||||||
let node_id = self.element_to_node_id(id);
|
|
||||||
let node = self.tree.get_mut(node_id).unwrap();
|
|
||||||
if let NodeType::Element { attributes, .. } = &mut node.node_data.node_type {
|
|
||||||
attributes.insert(
|
|
||||||
OwnedAttributeDiscription {
|
|
||||||
name: name.to_string(),
|
|
||||||
namespace: None,
|
|
||||||
volatile: false,
|
|
||||||
},
|
|
||||||
crate::node::OwnedAttributeValue::Bool(value),
|
|
||||||
);
|
);
|
||||||
mark_dirty(
|
mark_dirty(
|
||||||
node_id,
|
node_id,
|
||||||
|
|
|
@ -211,7 +211,7 @@ pub enum ElementAttr {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementAttr {
|
impl ElementAttr {
|
||||||
pub fn flart(&self) -> Span {
|
pub fn start(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
ElementAttr::AttrText { name, .. } => name.span(),
|
ElementAttr::AttrText { name, .. } => name.span(),
|
||||||
ElementAttr::AttrExpression { name, .. } => name.span(),
|
ElementAttr::AttrExpression { name, .. } => name.span(),
|
||||||
|
@ -265,7 +265,7 @@ impl ToTokens for ElementAttrNamed {
|
||||||
ElementAttr::CustomAttrText { name, value } => {
|
ElementAttr::CustomAttrText { name, value } => {
|
||||||
quote! {
|
quote! {
|
||||||
__cx.attr(
|
__cx.attr(
|
||||||
dioxus_elements::#el_name::#name.0,
|
#name,
|
||||||
#value,
|
#value,
|
||||||
None,
|
None,
|
||||||
false
|
false
|
||||||
|
@ -275,7 +275,7 @@ impl ToTokens for ElementAttrNamed {
|
||||||
ElementAttr::CustomAttrExpression { name, value } => {
|
ElementAttr::CustomAttrExpression { name, value } => {
|
||||||
quote! {
|
quote! {
|
||||||
__cx.attr(
|
__cx.attr(
|
||||||
dioxus_elements::#el_name::#name.0,
|
#name,
|
||||||
#value,
|
#value,
|
||||||
None,
|
None,
|
||||||
false
|
false
|
||||||
|
|
|
@ -79,7 +79,7 @@ impl NodeDepState for Focus {
|
||||||
if let Some(index) = a
|
if let Some(index) = a
|
||||||
.value
|
.value
|
||||||
.as_int()
|
.as_int()
|
||||||
.or_else(|| a.value.as_text().and_then(|v| v.parse::<i32>().ok()))
|
.or_else(|| a.value.as_text().and_then(|v| v.parse::<i64>().ok()))
|
||||||
{
|
{
|
||||||
match index.cmp(&0) {
|
match index.cmp(&0) {
|
||||||
Ordering::Less => FocusLevel::Unfocusable,
|
Ordering::Less => FocusLevel::Unfocusable,
|
||||||
|
|
|
@ -75,10 +75,21 @@ impl WebsysDom {
|
||||||
value,
|
value,
|
||||||
id,
|
id,
|
||||||
ns,
|
ns,
|
||||||
} => i.SetAttribute(id.0 as u32, name, value.into(), ns),
|
} => match value {
|
||||||
SetBoolAttribute { name, value, id } => {
|
dioxus_core::AttributeValue::Text(txt) => {
|
||||||
i.SetBoolAttribute(id.0 as u32, name, value)
|
i.SetAttribute(id.0 as u32, name, txt.into(), ns)
|
||||||
}
|
}
|
||||||
|
dioxus_core::AttributeValue::Float(f) => {
|
||||||
|
i.SetAttribute(id.0 as u32, name, f.into(), ns)
|
||||||
|
}
|
||||||
|
dioxus_core::AttributeValue::Int(n) => {
|
||||||
|
i.SetAttribute(id.0 as u32, name, n.into(), ns)
|
||||||
|
}
|
||||||
|
dioxus_core::AttributeValue::Bool(b) => {
|
||||||
|
i.SetBoolAttribute(id.0 as u32, name, b)
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
SetText { value, id } => i.SetText(id.0 as u32, value.into()),
|
SetText { value, id } => i.SetText(id.0 as u32, value.into()),
|
||||||
NewEventListener { name, id, .. } => {
|
NewEventListener { name, id, .. } => {
|
||||||
self.interpreter.NewEventListener(
|
self.interpreter.NewEventListener(
|
||||||
|
|
|
@ -187,10 +187,12 @@ 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.load_templates(&edits.templates);
|
websys_dom.load_templates(&edits.templates);
|
||||||
websys_dom.apply_edits(edits.edits);
|
websys_dom.apply_edits(edits.edits);
|
||||||
|
}
|
||||||
|
|
||||||
// the mutations come back with nothing - we need to actually mount them
|
// the mutations come back with nothing - we need to actually mount them
|
||||||
websys_dom.mount();
|
websys_dom.mount();
|
||||||
|
|
Loading…
Add table
Reference in a new issue