Ver Fonte

create scafolding for the shadow dom

Evan Almloff há 2 anos atrás
pai
commit
b2c87f5c47

+ 134 - 0
packages/native-core/src/custom_element.rs

@@ -0,0 +1,134 @@
+use std::{
+    cell::RefCell,
+    sync::{Arc, RwLock},
+};
+
+use rustc_hash::FxHashMap;
+use shipyard::Component;
+
+use crate::{
+    node::{FromAnyValue, NodeType},
+    node_ref::AttributeMask,
+    prelude::{NodeImmutable, NodeMut, RealDom},
+    real_dom::NodeTypeMut,
+    shadow_dom::ShadowDom,
+    NodeId,
+};
+
+#[derive(Default)]
+pub(crate) struct CustomElementRegistry<V: FromAnyValue + Send + Sync> {
+    builders: FxHashMap<&'static str, CustomElementBuilder<V>>,
+}
+
+impl<V: FromAnyValue + Send + Sync> CustomElementRegistry<V> {
+    pub fn register<W: CustomElement<V>>(&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<V>) {
+        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<V: FromAnyValue + Send + Sync> {
+    create: fn(&mut RealDom<V>, NodeId) -> Box<dyn CustomElementUpdater<V>>,
+}
+
+/// A controlled element that renders to a shadow DOM
+pub trait CustomElement<V: FromAnyValue + Send + Sync = ()>: 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<V>, 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<V>, _attributes: &AttributeMask);
+}
+
+/// A factory for creating widgets
+trait ElementFactory<W: CustomElementUpdater<V>, 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<V>, light_root_id: NodeId) -> W;
+}
+
+impl<W: CustomElement<V>, V: FromAnyValue + Send + Sync> ElementFactory<W, V> for W {
+    const NAME: &'static str = W::NAME;
+
+    fn create(dom: &mut RealDom<V>, light_root_id: NodeId) -> Self {
+        Self::create(dom, light_root_id)
+    }
+}
+
+/// A trait for updating widgets
+trait CustomElementUpdater<V: FromAnyValue + Send + Sync = ()>: Send + Sync + 'static {
+    /// Called when the attributes of the widget are changed.
+    fn attributes_changed(&mut self, _dom: &mut RealDom<V>, _attributes: &AttributeMask);
+
+    /// The root node of the widget.
+    fn root(&self) -> NodeId;
+}
+
+impl<W: CustomElement<V>, V: FromAnyValue + Send + Sync> CustomElementUpdater<V> for W {
+    fn attributes_changed(&mut self, root: &mut RealDom<V>, attributes: &AttributeMask) {
+        self.attributes_changed(root, attributes);
+    }
+
+    fn root(&self) -> NodeId {
+        self.root()
+    }
+}
+
+pub struct CustomElementManager<V: FromAnyValue = ()> {
+    inner: Arc<RwLock<Box<dyn CustomElementUpdater<V>>>>,
+}
+
+impl<V: FromAnyValue + Send + Sync> CustomElementManager<V> {
+    pub fn root(&self) -> NodeId {
+        self.inner.read().unwrap().root()
+    }
+}
+
+impl<V: FromAnyValue> Clone for CustomElementManager<V> {
+    fn clone(&self) -> Self {
+        Self {
+            inner: self.inner.clone(),
+        }
+    }
+}

+ 1 - 0
packages/native-core/src/dioxus.rs

@@ -262,6 +262,7 @@ fn create_template_node<V: FromAnyValue + Send + Sync>(
                     })
                     })
                     .collect(),
                     .collect(),
                 listeners: FxHashSet::default(),
                 listeners: FxHashSet::default(),
+                shadow: None,
             });
             });
             let node_id = rdom.create_node(node).id();
             let node_id = rdom.create_node(node).id();
             for child in *children {
             for child in *children {

+ 3 - 0
packages/native-core/src/lib.rs

@@ -7,6 +7,7 @@ use std::hash::BuildHasherDefault;
 use node_ref::NodeMask;
 use node_ref::NodeMask;
 use rustc_hash::FxHasher;
 use rustc_hash::FxHasher;
 
 
+pub mod custom_element;
 #[cfg(feature = "dioxus")]
 #[cfg(feature = "dioxus")]
 pub mod dioxus;
 pub mod dioxus;
 pub mod layout_attributes;
 pub mod layout_attributes;
@@ -15,8 +16,10 @@ pub mod node_ref;
 pub mod node_watcher;
 pub mod node_watcher;
 mod passes;
 mod passes;
 pub mod real_dom;
 pub mod real_dom;
+mod shadow_dom;
 pub mod tree;
 pub mod tree;
 pub mod utils;
 pub mod utils;
+
 pub use shipyard::EntityId as NodeId;
 pub use shipyard::EntityId as NodeId;
 
 
 pub mod exports {
 pub mod exports {

+ 6 - 0
packages/native-core/src/node.rs

@@ -7,6 +7,8 @@ use std::{
     fmt::{Debug, Display},
     fmt::{Debug, Display},
 };
 };
 
 
+use crate::shadow_dom::ShadowDom;
+
 /// A element node in the RealDom
 /// A element node in the RealDom
 #[derive(Debug, Clone, Default)]
 #[derive(Debug, Clone, Default)]
 pub struct ElementNode<V: FromAnyValue = ()> {
 pub struct ElementNode<V: FromAnyValue = ()> {
@@ -18,6 +20,9 @@ pub struct ElementNode<V: FromAnyValue = ()> {
     pub attributes: FxHashMap<OwnedAttributeDiscription, OwnedAttributeValue<V>>,
     pub attributes: FxHashMap<OwnedAttributeDiscription, OwnedAttributeValue<V>>,
     /// The events the element is listening for
     /// The events the element is listening for
     pub listeners: FxHashSet<String>,
     pub listeners: FxHashSet<String>,
+    /// The shadow dom of the element
+    // This is a rare property, so it is stored in an `Box` to save space
+    pub shadow: Option<Box<ShadowDom<V>>>,
 }
 }
 
 
 impl ElementNode {
 impl ElementNode {
@@ -28,6 +33,7 @@ impl ElementNode {
             namespace: namespace.into(),
             namespace: namespace.into(),
             attributes: Default::default(),
             attributes: Default::default(),
             listeners: Default::default(),
             listeners: Default::default(),
+            shadow: Default::default(),
         }
         }
     }
     }
 }
 }

+ 3 - 0
packages/native-core/src/passes.rs

@@ -104,6 +104,9 @@ pub trait State<V: FromAnyValue + Send + Sync = ()>: Any + Send + Sync {
     /// This is a mask of what aspects of the node are required to update this state
     /// This is a mask of what aspects of the node are required to update this state
     const NODE_MASK: NodeMaskBuilder<'static>;
     const NODE_MASK: NodeMaskBuilder<'static>;
 
 
+    /// Does the state traverse into the shadow dom?
+    const TRAVERSE_SHADOW_DOM: bool = false;
+
     /// Update this state in a node, returns if the state was updated
     /// Update this state in a node, returns if the state was updated
     fn update<'a>(
     fn update<'a>(
         &mut self,
         &mut self,

+ 12 - 0
packages/native-core/src/real_dom.rs

@@ -17,6 +17,7 @@ use crate::node_ref::{NodeMask, NodeMaskBuilder};
 use crate::node_watcher::{AttributeWatcher, NodeWatcher};
 use crate::node_watcher::{AttributeWatcher, NodeWatcher};
 use crate::passes::{DirtyNodeStates, TypeErasedState};
 use crate::passes::{DirtyNodeStates, TypeErasedState};
 use crate::prelude::AttributeMaskBuilder;
 use crate::prelude::AttributeMaskBuilder;
+use crate::shadow_dom::ShadowDom;
 use crate::tree::{TreeMut, TreeMutView, TreeRef, TreeRefView};
 use crate::tree::{TreeMut, TreeMutView, TreeRef, TreeRefView};
 use crate::NodeId;
 use crate::NodeId;
 use crate::{FxDashSet, SendAnyMap};
 use crate::{FxDashSet, SendAnyMap};
@@ -139,6 +140,7 @@ impl<V: FromAnyValue + Send + Sync> RealDom<V> {
             namespace: Some("Root".to_string()),
             namespace: Some("Root".to_string()),
             attributes: FxHashMap::default(),
             attributes: FxHashMap::default(),
             listeners: FxHashSet::default(),
             listeners: FxHashSet::default(),
+            shadow: None,
         });
         });
         let root_id = world.add_entity(root_node);
         let root_id = world.add_entity(root_node);
         {
         {
@@ -1056,6 +1058,16 @@ impl<V: FromAnyValue + Send + Sync> ElementNodeMut<'_, V> {
     pub fn listeners(&self) -> &FxHashSet<String> {
     pub fn listeners(&self) -> &FxHashSet<String> {
         &self.element().listeners
         &self.element().listeners
     }
     }
+
+    /// Get the shadow root of the element
+    pub fn shadow_root(&self) -> Option<&ShadowDom<V>> {
+        self.element().shadow.as_deref()
+    }
+
+    /// Get the shadow root of the element
+    pub fn shadow_root_mut(&mut self) -> &mut Option<Box<ShadowDom<V>>> {
+        &mut self.element_mut().shadow
+    }
 }
 }
 
 
 // Create a workload from all of the passes. This orders the passes so that each pass will only run at most once.
 // Create a workload from all of the passes. This orders the passes so that each pass will only run at most once.

+ 26 - 0
packages/native-core/src/shadow_dom.rs

@@ -0,0 +1,26 @@
+use std::fmt::Debug;
+
+use crate::{custom_element::CustomElementManager, node::FromAnyValue, prelude::NodeRef, NodeId};
+
+#[derive(Clone)]
+pub struct ShadowDom<V: FromAnyValue> {
+    shadow_root: NodeId,
+    updater: CustomElementManager<V>,
+}
+
+impl<V: FromAnyValue + Send + Sync> ShadowDom<V> {
+    pub fn new(updater: CustomElementManager<V>) -> Self {
+        Self {
+            shadow_root: updater.root(),
+            updater,
+        }
+    }
+}
+
+impl<V: FromAnyValue> Debug for ShadowDom<V> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("ShadowDom")
+            .field("shadow_root", &self.shadow_root)
+            .finish()
+    }
+}

+ 0 - 1
packages/native-core/src/utils/mod.rs

@@ -5,4 +5,3 @@
 mod persistant_iterator;
 mod persistant_iterator;
 pub use persistant_iterator::*;
 pub use persistant_iterator::*;
 pub mod cursor;
 pub mod cursor;
-pub mod widget_watcher;

+ 0 - 136
packages/native-core/src/utils/widget_watcher.rs

@@ -1,136 +0,0 @@
-//! Widget utilities for defining, registering and updating widgets
-
-use std::sync::{Arc, RwLock};
-
-use rustc_hash::FxHashMap;
-use shipyard::Component;
-
-use crate::{
-    node::{FromAnyValue, NodeType},
-    node_ref::AttributeMask,
-    node_watcher::{AttributeWatcher, NodeWatcher},
-    prelude::{NodeImmutable, NodeMut, RealDom},
-    NodeId,
-};
-
-/// A watcher that handlers registering and updating widgets
-#[derive(Default, Clone)]
-pub struct WidgetWatcher<V: FromAnyValue + Send + Sync> {
-    inner: Arc<RwLock<WidgetWatcherInner<V>>>,
-}
-
-impl<V: FromAnyValue + Send + Sync> NodeWatcher<V> for WidgetWatcher<V> {
-    fn on_node_added(&mut self, node: NodeMut<V>) {
-        let mut inner = self.inner.write().unwrap();
-        inner.on_node_added(node);
-    }
-
-    fn on_node_removed(&mut self, node: NodeMut<V>) {
-        let mut inner = self.inner.write().unwrap();
-        inner.on_node_removed(node);
-    }
-}
-
-impl<V: FromAnyValue + Send + Sync> WidgetWatcher<V> {
-    /// Register a widget
-    pub fn register_widget<W: WidgetFactory<O, V> + 'static, O: WidgetUpdater<V>>(&mut self) {
-        let mut inner = self.inner.write().unwrap();
-        inner.builders.insert(
-            W::NAME,
-            WidgetBuilder {
-                create: |mut node| Box::new(W::create(&mut node)),
-            },
-        );
-    }
-
-    /// Attach the widget watcher to the RealDom
-    pub fn attach(&self, dom: &mut RealDom<V>) {
-        dom.add_node_watcher(self.clone());
-        dom.add_attribute_watcher(self.clone());
-    }
-}
-
-impl<V: FromAnyValue + Send + Sync> AttributeWatcher<V> for WidgetWatcher<V> {
-    fn on_attributes_changed(&self, node: NodeMut<V>, attributes: &AttributeMask) {
-        let mut inner = self.inner.write().unwrap();
-        if let Some(widget) = inner.widgets.get_mut(&node.id()) {
-            widget.dyn_widget.attributes_changed(node, attributes);
-        }
-    }
-}
-
-#[derive(Default)]
-struct WidgetWatcherInner<V: FromAnyValue + Send + Sync> {
-    builders: FxHashMap<&'static str, WidgetBuilder<V>>,
-    widgets: FxHashMap<NodeId, BoxedWidget<V>>,
-}
-
-impl<V: FromAnyValue + Send + Sync> NodeWatcher<V> for WidgetWatcherInner<V> {
-    fn on_node_added(&mut self, node: NodeMut<V>) {
-        let node_type = node.node_type();
-        if let NodeType::Element(el) = &*node_type {
-            if let Some(builder) = self.builders.get(el.tag.as_str()) {
-                drop(node_type);
-                let id = node.id();
-                let widget = (builder.create)(node);
-                self.widgets.insert(id, BoxedWidget { dyn_widget: widget });
-            }
-        }
-    }
-
-    fn on_node_removed(&mut self, node: NodeMut<V>) {
-        self.widgets.remove(&node.id());
-    }
-}
-
-#[derive(Component)]
-struct BoxedWidget<V: FromAnyValue + Send + Sync> {
-    dyn_widget: Box<dyn WidgetUpdater<V>>,
-}
-
-struct WidgetBuilder<V: FromAnyValue + Send + Sync> {
-    create: fn(NodeMut<V>) -> Box<dyn WidgetUpdater<V>>,
-}
-
-/// A controlled element (a.k.a. widget)
-pub trait Widget<V: FromAnyValue + Send + Sync = ()>: Send + Sync + 'static {
-    /// The tag the widget is registered under.
-    const NAME: &'static str;
-
-    /// Create a new widget.
-    fn create(root: &mut NodeMut<V>) -> Self;
-
-    /// Called when the attributes of the widget are changed.
-    fn attributes_changed(&mut self, _root: NodeMut<V>, _attributes: &AttributeMask);
-}
-
-/// A factory for creating widgets
-pub trait WidgetFactory<W: WidgetUpdater<V>, V: FromAnyValue + Send + Sync = ()>:
-    Send + Sync + 'static
-{
-    /// The tag the widget is registered under.
-    const NAME: &'static str;
-
-    /// Create a new widget.
-    fn create(root: &mut NodeMut<V>) -> W;
-}
-
-impl<W: Widget<V>, V: FromAnyValue + Send + Sync> WidgetFactory<W, V> for W {
-    const NAME: &'static str = W::NAME;
-
-    fn create(root: &mut NodeMut<V>) -> Self {
-        W::create(root)
-    }
-}
-
-/// A trait for updating widgets
-pub trait WidgetUpdater<V: FromAnyValue + Send + Sync = ()>: Send + Sync + 'static {
-    /// Called when the attributes of the widget are changed.
-    fn attributes_changed(&mut self, _root: NodeMut<V>, _attributes: &AttributeMask);
-}
-
-impl<W: Widget<V>, V: FromAnyValue + Send + Sync> WidgetUpdater<V> for W {
-    fn attributes_changed(&mut self, root: NodeMut<V>, attributes: &AttributeMask) {
-        self.attributes_changed(root, attributes);
-    }
-}

+ 1 - 0
packages/native-core/tests/passes.rs

@@ -10,6 +10,7 @@ fn create_blank_element() -> NodeType {
         namespace: None,
         namespace: None,
         attributes: FxHashMap::default(),
         attributes: FxHashMap::default(),
         listeners: FxHashSet::default(),
         listeners: FxHashSet::default(),
+        shadow: None,
     })
     })
 }
 }