use std::sync::{Arc, RwLock}; use rustc_hash::FxHashMap; use crate::{ node::{FromAnyValue, NodeType}, node_ref::AttributeMask, prelude::{NodeImmutable, NodeMut, RealDom}, real_dom::NodeTypeMut, shadow_dom::ShadowDom, NodeId, }; pub(crate) struct CustomElementRegistry { builders: FxHashMap<&'static str, CustomElementBuilder>, } impl Default for CustomElementRegistry { fn default() -> Self { Self { builders: FxHashMap::default(), } } } impl CustomElementRegistry { pub fn register>(&mut self) { self.builders.insert( W::NAME, CustomElementBuilder { create: |dom, light_root_id| Box::new(W::create(dom, light_root_id)), }, ); } pub fn add_shadow_dom(&self, mut node: NodeMut) { let element_tag = if let NodeType::Element(el) = &*node.node_type() { Some(el.tag.clone()) } else { None }; if let Some(element_tag) = element_tag { if let Some(builder) = self.builders.get(element_tag.as_str()) { let boxed_widget = { let light_root_id = node.id(); let dom = node.real_dom_mut(); (builder.create)(dom, light_root_id) }; let boxed_widget = CustomElementManager { inner: Arc::new(RwLock::new(boxed_widget)), }; let NodeTypeMut::Element(mut el) = node.node_type_mut()else{ panic!("The type of the light element should not change when creating a shadow DOM") }; *el.shadow_root_mut() = Some(ShadowDom::new(boxed_widget).into()); } } } } struct CustomElementBuilder { create: fn(&mut RealDom, NodeId) -> Box>, } /// A controlled element that renders to a shadow DOM pub trait CustomElement: Send + Sync + 'static { /// The tag the widget is registered under. const NAME: &'static str; /// Create a new widget without mounting it. fn create(dom: &mut RealDom, light_root_id: NodeId) -> Self; /// The root node of the widget. fn root(&self) -> NodeId; /// Called when the attributes of the widget are changed. fn attributes_changed(&mut self, _dom: &mut RealDom, _attributes: &AttributeMask); } /// A factory for creating widgets trait ElementFactory, V: FromAnyValue + Send + Sync = ()>: Send + Sync + 'static { /// The tag the widget is registered under. const NAME: &'static str; /// Create a new widget. fn create(dom: &mut RealDom, light_root_id: NodeId) -> W; } impl, V: FromAnyValue + Send + Sync> ElementFactory for W { const NAME: &'static str = W::NAME; fn create(dom: &mut RealDom, light_root_id: NodeId) -> Self { Self::create(dom, light_root_id) } } /// A trait for updating widgets trait CustomElementUpdater: Send + Sync + 'static { /// Called when the attributes of the widget are changed. fn attributes_changed(&mut self, dom: &mut RealDom, attributes: &AttributeMask); /// The root node of the widget. fn root(&self) -> NodeId; } impl, V: FromAnyValue + Send + Sync> CustomElementUpdater for W { fn attributes_changed(&mut self, root: &mut RealDom, attributes: &AttributeMask) { self.attributes_changed(root, attributes); } fn root(&self) -> NodeId { self.root() } } pub struct CustomElementManager { inner: Arc>>>, } impl CustomElementManager { pub fn root(&self) -> NodeId { self.inner.read().unwrap().root() } } impl Clone for CustomElementManager { fn clone(&self) -> Self { Self { inner: self.inner.clone(), } } }