use std::any::Any;
use std::collections::HashMap;
use markup5ever::LocalName;
use crate::DocumentMutator;
use crate::node::ShadowRootMode;
use blitz_traits::node_id::NodeId;
pub type CustomElementFactory = Box<dyn Fn() -> Box<dyn CustomElement>>;
pub struct CustomElementDefinition {
pub factory: CustomElementFactory,
pub mode: ShadowRootMode,
pub observed_attributes: Vec<LocalName>,
}
impl CustomElementDefinition {
pub fn new(factory: impl Fn() -> Box<dyn CustomElement> + 'static) -> Self {
Self {
factory: Box::new(factory),
mode: ShadowRootMode::Open,
observed_attributes: Vec::new(),
}
}
pub fn with_mode(mut self, mode: ShadowRootMode) -> Self {
self.mode = mode;
self
}
pub fn observing(mut self, attributes: impl IntoIterator<Item = LocalName>) -> Self {
self.observed_attributes = attributes.into_iter().collect();
self
}
pub fn observes(&self, name: &LocalName) -> bool {
self.observed_attributes.is_empty() || self.observed_attributes.contains(name)
}
}
#[derive(Default)]
pub struct CustomElementRegistry {
definitions: HashMap<LocalName, CustomElementDefinition>,
}
impl CustomElementRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn define(&mut self, name: LocalName, definition: CustomElementDefinition) {
self.definitions.insert(name, definition);
}
pub fn get(&self, name: &LocalName) -> Option<&CustomElementDefinition> {
self.definitions.get(name)
}
pub fn contains(&self, name: &LocalName) -> bool {
self.definitions.contains_key(name)
}
}
pub struct CustomElementData {
pub(crate) controller: Option<Box<dyn CustomElement>>,
pub upgraded: bool,
}
impl CustomElementData {
pub(crate) fn new(controller: Box<dyn CustomElement>) -> Self {
Self {
controller: Some(controller),
upgraded: false,
}
}
}
pub struct CustomElementCtx<'a, 'doc> {
pub(crate) mutator: &'a mut DocumentMutator<'doc>,
pub(crate) host_id: NodeId,
pub(crate) shadow_root_id: NodeId,
}
impl<'a, 'doc> CustomElementCtx<'a, 'doc> {
pub fn host_id(&self) -> NodeId {
self.host_id
}
pub fn shadow_root_id(&self) -> NodeId {
self.shadow_root_id
}
pub fn mutator(&mut self) -> &mut DocumentMutator<'doc> {
self.mutator
}
pub fn host_attr(&self, name: impl PartialEq<LocalName>) -> Option<String> {
self.mutator
.doc
.get_node(self.host_id)
.and_then(|node| node.element_data())
.and_then(|el| el.attr(name))
.map(|s| s.to_string())
}
pub fn clear_shadow(&mut self) {
self.mutator
.remove_and_drop_all_children(self.shadow_root_id);
}
pub fn set_shadow_html(&mut self, html: &str) {
self.clear_shadow();
self.mutator.set_inner_html(self.shadow_root_id, html);
}
pub fn append_shadow_children(&mut self, child_ids: &[NodeId]) {
self.mutator.append_children(self.shadow_root_id, child_ids);
}
}
pub trait CustomElement: Any {
fn connected(&mut self, ctx: &mut CustomElementCtx<'_, '_>) {
let _ = ctx;
}
fn disconnected(&mut self, ctx: &mut CustomElementCtx<'_, '_>) {
let _ = ctx;
}
fn attribute_changed(
&mut self,
ctx: &mut CustomElementCtx<'_, '_>,
name: &str,
old_value: Option<&str>,
new_value: Option<&str>,
) {
let _ = (ctx, name, old_value, new_value);
}
}