use std::sync::{Arc, RwLock}; use rustc_hash::FxHashMap; use shipyard::Component; use crate::{ node::{FromAnyValue, NodeType}, node_ref::AttributeMask, prelude::{NodeImmutable, NodeMut, RealDom}, 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| Box::new(W::create(dom)), }, ); } 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 dom = node.real_dom_mut(); (builder.create)(dom) }; let boxed_widget = CustomElementManager { inner: Arc::new(RwLock::new(boxed_widget)), }; node.insert(boxed_widget); } } } } struct CustomElementBuilder { create: fn(&mut RealDom) -> 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) -> Self; /// The root node of the widget. This must be static once the element is created. fn root(&self) -> NodeId; /// The slot to render children of the element into. This must be static once the element is created. fn slot(&self) -> Option { None } /// 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) -> W; } impl, V: FromAnyValue + Send + Sync> ElementFactory for W { const NAME: &'static str = W::NAME; fn create(dom: &mut RealDom) -> Self { Self::create(dom) } } /// 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() } } #[derive(Component, Clone)] pub struct CustomElementManager { inner: Arc>>>, } impl CustomElementManager { pub fn root(&self) -> NodeId { self.inner.read().unwrap().root() } pub fn on_attributes_changed(&self, dom: &mut RealDom, attributes: &AttributeMask) { self.inner .write() .unwrap() .attributes_changed(dom, attributes); } }