create scafolding for the shadow dom
This commit is contained in:
1 parent
8b5f2cb02f
commit
b2c87f5c47
10 files changed
+186
-137
No files matched your search
@@ -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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user