use crate::*;
impl PartialEq for TextNode {
fn eq(&self, other: &Self) -> bool {
self.get_content() == other.get_content()
}
}
impl PartialEq for VirtualNode {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(VirtualNode::Text(a_text), VirtualNode::Text(b_text)) => a_text == b_text,
(
VirtualNode::Element {
tag: a_tag,
attributes: a_attrs,
children: a_children,
..
},
VirtualNode::Element {
tag: b_tag,
attributes: b_attrs,
children: b_children,
..
},
) => {
a_tag == b_tag
&& a_attrs.len() == b_attrs.len()
&& a_attrs.iter().zip(b_attrs.iter()).all(|(a, b)| a == b)
&& a_children.len() == b_children.len()
&& a_children
.iter()
.zip(b_children.iter())
.all(|(a, b)| a == b)
}
(VirtualNode::Fragment(a_children), VirtualNode::Fragment(b_children)) => {
a_children.len() == b_children.len()
&& a_children
.iter()
.zip(b_children.iter())
.all(|(a, b)| a == b)
}
(VirtualNode::Dynamic(_), VirtualNode::Dynamic(_)) => false,
(VirtualNode::Empty, VirtualNode::Empty) => true,
_ => false,
}
}
}
impl Default for DynamicNode {
fn default() -> Self {
let node: DynamicNode = DynamicNode {
render_fn: Rc::new(RefCell::new(|| VirtualNode::Empty)),
hook_context: HookContext::default(),
};
node
}
}
impl Clone for DynamicNode {
fn clone(&self) -> Self {
DynamicNode {
render_fn: Rc::clone(self.get_render_fn()),
hook_context: self.get_hook_context(),
}
}
}
impl VirtualNode {
pub fn needs_patch(old: &VirtualNode, new: &VirtualNode) -> bool {
match (old, new) {
(VirtualNode::Text(old_text), VirtualNode::Text(new_text)) => {
old_text.get_content() != new_text.get_content()
}
(
VirtualNode::Element {
tag: old_tag,
attributes: old_attrs,
children: old_children,
key: _old_key,
},
VirtualNode::Element {
tag: new_tag,
attributes: new_attrs,
children: new_children,
key: _new_key,
},
) => {
if old_tag != new_tag {
return true;
}
if old_attrs.len() != new_attrs.len() {
return true;
}
for (old_attr, new_attr) in old_attrs.iter().zip(new_attrs.iter()) {
if old_attr.get_name() != new_attr.get_name()
|| old_attr.get_value() != new_attr.get_value()
{
return true;
}
}
if old_children.len() != new_children.len() {
return true;
}
for (old_child, new_child) in old_children.iter().zip(new_children.iter()) {
if Self::needs_patch(old_child, new_child) {
return true;
}
}
false
}
(VirtualNode::Fragment(old_children), VirtualNode::Fragment(new_children)) => {
if old_children.len() != new_children.len() {
return true;
}
for (old_child, new_child) in old_children.iter().zip(new_children.iter()) {
if Self::needs_patch(old_child, new_child) {
return true;
}
}
false
}
(VirtualNode::Dynamic(_), VirtualNode::Dynamic(_)) => false,
(VirtualNode::Empty, VirtualNode::Empty) => false,
_ => true,
}
}
pub fn get_element_node(tag_name: &str) -> Self {
VirtualNode::Element {
tag: Tag::Element(tag_name.to_string()),
attributes: Vec::new(),
children: Vec::new(),
key: None,
}
}
pub fn get_text_node(content: &str) -> Self {
VirtualNode::Text(TextNode::new(content.to_string(), None))
}
pub fn with_attribute(mut self, name: &str, value: AttributeValue) -> Self {
if let VirtualNode::Element {
ref mut attributes, ..
} = self
{
attributes.push(AttributeEntry::new(name.to_string(), value));
}
self
}
pub fn with_child(mut self, child: VirtualNode) -> Self {
if let VirtualNode::Element {
ref mut children, ..
} = self
{
children.push(child);
}
self
}
pub fn is_component(&self) -> bool {
matches!(
self,
VirtualNode::Element {
tag: Tag::Component(_),
..
}
)
}
pub fn tag_name(&self) -> Option<String> {
match self {
VirtualNode::Element { tag, .. } => match tag {
Tag::Element(name) => Some(name.clone()),
Tag::Component(name) => Some(name.clone()),
},
_ => None,
}
}
pub fn try_get_prop(&self, name: &str) -> Option<String> {
if let VirtualNode::Element { attributes, .. } = self {
for attr in attributes {
if attr.get_name() == name {
match attr.get_value() {
AttributeValue::Text(value) => return Some(value.clone()),
AttributeValue::Signal(signal) => return Some(signal.get()),
AttributeValue::Dynamic(value) => return Some(value.clone()),
_ => {}
}
}
}
}
None
}
pub fn try_get_typed_prop<T>(&self, name: &str) -> Option<T>
where
T: std::str::FromStr,
{
if let VirtualNode::Element { attributes, .. } = self {
for attr in attributes {
if attr.get_name() == name {
let raw: String = match attr.get_value() {
AttributeValue::Text(value) => value.clone(),
AttributeValue::Signal(signal) => signal.get(),
AttributeValue::Dynamic(value) => value.clone(),
_ => continue,
};
return raw.parse::<T>().ok();
}
}
}
None
}
pub fn try_get_signal_prop(&self, name: &str) -> Option<Signal<String>> {
if let VirtualNode::Element { attributes, .. } = self {
for attr in attributes {
if attr.get_name() == name
&& let AttributeValue::Signal(signal) = attr.get_value()
{
return Some(*signal);
}
}
}
None
}
pub fn get_children(&self) -> Vec<VirtualNode> {
if let VirtualNode::Element { children, .. } = self {
children.clone()
} else {
Vec::new()
}
}
pub fn try_get_text(&self) -> Option<String> {
match self {
VirtualNode::Text(text_node) => Some(text_node.get_content().clone()),
VirtualNode::Element { children, .. } => {
children.first().and_then(VirtualNode::try_get_text)
}
_ => None,
}
}
pub fn try_get_event(&self, name: &str) -> Option<NativeEventHandler> {
if let VirtualNode::Element { attributes, .. } = self {
for attr in attributes {
if attr.get_name() == name
&& let AttributeValue::Event(handler) = attr.get_value()
{
return Some(handler.clone());
}
}
}
None
}
pub fn try_get_callback(&self, name: &str) -> Option<NativeEventHandler> {
if let VirtualNode::Element { attributes, .. } = self {
for attr in attributes {
if attr.get_name() == name
&& let AttributeValue::Event(handler) = attr.get_value()
{
return Some(handler.clone());
}
}
}
None
}
}