use std::str::FromStr;
use crate::{tags::TagName, tree::TreeMut};
use dioxus_core::{AttributeValue, ElementId, TemplateNode, WriteMutations};
use rustc_hash::{FxHashMap, FxHashSet};
use shipyard::Component;
use crate::{
node::{ElementNode, FromAnyValue, NodeType, OwnedAttributeValue},
prelude::*,
real_dom::NodeTypeMut,
NodeId,
};
#[derive(Component)]
struct ElementIdComponent(ElementId);
pub struct DioxusState {
templates: FxHashMap<String, Vec<NodeId>>,
stack: Vec<NodeId>,
node_id_mapping: Vec<Option<NodeId>>,
}
impl DioxusState {
pub fn create<V: FromAnyValue + Send + Sync>(rdom: &mut RealDom<V>) -> Self {
let root_id = rdom.root_id();
let mut root = rdom.get_mut(root_id).unwrap();
root.insert(ElementIdComponent(ElementId(0)));
Self {
templates: FxHashMap::default(),
stack: vec![root_id],
node_id_mapping: vec![Some(root_id)],
}
}
pub fn element_to_node_id(&self, element_id: ElementId) -> NodeId {
self.try_element_to_node_id(element_id).unwrap()
}
pub fn try_element_to_node_id(&self, element_id: ElementId) -> Option<NodeId> {
self.node_id_mapping.get(element_id.0).copied().flatten()
}
pub fn create_mutation_writer<'a, V: FromAnyValue + Send + Sync>(
&'a mut self,
rdom: &'a mut RealDom<V>,
) -> DioxusNativeCoreMutationWriter<'a, V> {
DioxusNativeCoreMutationWriter { rdom, state: self }
}
fn set_element_id<V: FromAnyValue + Send + Sync>(
&mut self,
mut node: NodeMut<V>,
element_id: ElementId,
) {
let node_id = node.id();
node.insert(ElementIdComponent(element_id));
if self.node_id_mapping.len() <= element_id.0 {
self.node_id_mapping.resize(element_id.0 + 1, None);
} else if let Some(mut node) =
self.node_id_mapping[element_id.0].and_then(|id| node.real_dom_mut().get_mut(id))
{
node.remove();
}
self.node_id_mapping[element_id.0] = Some(node_id);
}
fn load_child<V: FromAnyValue + Send + Sync>(&self, rdom: &RealDom<V>, path: &[u8]) -> NodeId {
let mut current = rdom.get(*self.stack.last().unwrap()).unwrap();
for i in path {
let new_id = current.child_ids()[*i as usize];
current = rdom.get(new_id).unwrap();
}
current.id()
}
}
pub struct DioxusNativeCoreMutationWriter<'a, V: FromAnyValue + Send + Sync = ()> {
pub rdom: &'a mut RealDom<V>,
pub state: &'a mut DioxusState,
}
impl<V: FromAnyValue + Send + Sync> WriteMutations for DioxusNativeCoreMutationWriter<'_, V> {
fn register_template(&mut self, template: dioxus_core::prelude::Template) {
let mut template_root_ids = Vec::new();
for root in template.roots {
let id = create_template_node(self.rdom, root);
template_root_ids.push(id);
}
self.state
.templates
.insert(template.name.to_string(), template_root_ids);
}
fn append_children(&mut self, id: ElementId, m: usize) {
let children = self.state.stack.split_off(self.state.stack.len() - m);
let parent_id = self.state.element_to_node_id(id);
let mut parent = self.rdom.get_mut(parent_id).unwrap();
for child in children {
parent.add_child(child);
}
}
fn assign_node_id(&mut self, path: &'static [u8], id: ElementId) {
let node_id = self.state.load_child(self.rdom, path);
self.state
.set_element_id(self.rdom.get_mut(node_id).unwrap(), id);
}
fn create_placeholder(&mut self, id: ElementId) {
let node = NodeType::Placeholder;
let node = self.rdom.create_node(node);
let node_id = node.id();
self.state.set_element_id(node, id);
self.state.stack.push(node_id);
}
fn create_text_node(&mut self, value: &str, id: ElementId) {
let node_data = NodeType::Text(value.to_string());
let node = self.rdom.create_node(node_data);
let node_id = node.id();
self.state.set_element_id(node, id);
self.state.stack.push(node_id);
}
fn hydrate_text_node(&mut self, path: &'static [u8], value: &str, id: ElementId) {
let node_id = self.state.load_child(self.rdom, path);
let node = self.rdom.get_mut(node_id).unwrap();
self.state.set_element_id(node, id);
let mut node = self.rdom.get_mut(node_id).unwrap();
let node_type_mut = node.node_type_mut();
if let NodeTypeMut::Text(mut text) = node_type_mut {
*text.text_mut() = value.to_string();
} else {
drop(node_type_mut);
node.set_type(NodeType::Text(value.to_string()));
}
}
fn load_template(&mut self, name: &'static str, index: usize, id: ElementId) {
let template_id = self.state.templates[name][index];
let clone_id = self.rdom.get_mut(template_id).unwrap().clone_node();
let clone = self.rdom.get_mut(clone_id).unwrap();
self.state.set_element_id(clone, id);
self.state.stack.push(clone_id);
}
fn replace_node_with(&mut self, id: ElementId, m: usize) {
let new_nodes = self.state.stack.split_off(self.state.stack.len() - m);
let old_node_id = self.state.element_to_node_id(id);
for new in new_nodes {
let mut node = self.rdom.get_mut(new).unwrap();
node.insert_before(old_node_id);
}
self.rdom.get_mut(old_node_id).unwrap().remove();
}
fn replace_placeholder_with_nodes(&mut self, path: &'static [u8], m: usize) {
let new_nodes = self.state.stack.split_off(self.state.stack.len() - m);
let old_node_id = self.state.load_child(self.rdom, path);
for new in new_nodes {
let mut node = self.rdom.get_mut(new).unwrap();
node.insert_before(old_node_id);
}
self.rdom.get_mut(old_node_id).unwrap().remove();
}
fn insert_nodes_after(&mut self, id: ElementId, m: usize) {
let new_nodes = self.state.stack.split_off(self.state.stack.len() - m);
let old_node_id = self.state.element_to_node_id(id);
for new in new_nodes.into_iter().rev() {
let mut node = self.rdom.get_mut(new).unwrap();
node.insert_after(old_node_id);
}
}
fn insert_nodes_before(&mut self, id: ElementId, m: usize) {
let new_nodes = self.state.stack.split_off(self.state.stack.len() - m);
let old_node_id = self.state.element_to_node_id(id);
for new in new_nodes {
self.rdom.tree_mut().insert_before(old_node_id, new);
}
}
fn set_attribute(
&mut self,
name: &'static str,
_ns: Option<&'static str>,
value: &AttributeValue,
id: ElementId,
) {
let node_id = self.state.element_to_node_id(id);
let mut node = self.rdom.get_mut(node_id).unwrap();
let mut node_type_mut = node.node_type_mut();
if let NodeTypeMut::Element(element) = &mut node_type_mut {
let attribute = AttributeName::from_str(name).expect("Unexpected");
if let AttributeValue::None = &value {
element.remove_attribute(&attribute);
} else {
element.set_attribute(attribute, OwnedAttributeValue::from(value));
}
}
}
fn set_node_text(&mut self, value: &str, id: ElementId) {
let node_id = self.state.element_to_node_id(id);
let mut node = self.rdom.get_mut(node_id).unwrap();
let node_type_mut = node.node_type_mut();
if let NodeTypeMut::Text(mut text) = node_type_mut {
*text.text_mut() = value.to_string();
}
}
fn create_event_listener(&mut self, name: &'static str, id: ElementId) {
let node_id = self.state.element_to_node_id(id);
let mut node = self.rdom.get_mut(node_id).unwrap();
node.add_event_listener(EventName::from_str(name).expect("Unexpected."));
}
fn remove_event_listener(&mut self, name: &'static str, id: ElementId) {
let node_id = self.state.element_to_node_id(id);
let mut node = self.rdom.get_mut(node_id).unwrap();
node.remove_event_listener(&EventName::from_str(name).expect("Unexpected."));
}
fn remove_node(&mut self, id: ElementId) {
let node_id = self.state.element_to_node_id(id);
self.rdom.get_mut(node_id).unwrap().remove();
}
fn push_root(&mut self, id: ElementId) {
let node_id = self.state.element_to_node_id(id);
self.state.stack.push(node_id);
}
}
fn create_template_node<V: FromAnyValue + Send + Sync>(
rdom: &mut RealDom<V>,
node: &TemplateNode,
) -> NodeId {
match node {
TemplateNode::Element {
tag,
attrs,
children,
..
} => {
let node = NodeType::Element(ElementNode {
tag: TagName::from_str(tag).expect("Unexpected."),
attributes: attrs
.iter()
.filter_map(|attr| match attr {
dioxus_core::TemplateAttribute::Static { name, value, .. } => Some((
AttributeName::from_str(name).expect("Unexpected."),
OwnedAttributeValue::Text(value.to_string()),
)),
dioxus_core::TemplateAttribute::Dynamic { .. } => None,
})
.collect(),
listeners: FxHashSet::default(),
});
let node_id = rdom.create_node(node).id();
for child in *children {
let child_id = create_template_node(rdom, child);
rdom.get_mut(node_id).unwrap().add_child(child_id);
}
node_id
}
TemplateNode::Text { text } => rdom.create_node(NodeType::Text(text.to_string())).id(),
TemplateNode::Dynamic { .. } => rdom.create_node(NodeType::Placeholder).id(),
TemplateNode::DynamicText { .. } => {
rdom.create_node(NodeType::Text(String::default())).id()
}
}
}
pub trait NodeImmutableDioxusExt<V: FromAnyValue + Send + Sync>: NodeImmutable<V> {
fn mounted_id(&self) -> Option<ElementId> {
let id = self.get::<ElementIdComponent>();
id.map(|id| id.0)
}
}
impl<T: NodeImmutable<V>, V: FromAnyValue + Send + Sync> NodeImmutableDioxusExt<V> for T {}