Allow you to configure which element you inherit from.

This commit is contained in:
Jeremy Wall 2023-12-08 19:24:38 -05:00
parent 2b00383712
commit 5ba459bcb6
2 changed files with 51 additions and 19 deletions

View File

@ -33,14 +33,23 @@ fn expand_crate_ref(name: &str, path: Path) -> syn::Path {
} }
} }
struct AttributeConfig {
class_name: Literal,
element_name: Literal,
observed_attributes: Literal,
observed_events: Literal,
base_class: Literal,
}
fn get_class_and_element_names( fn get_class_and_element_names(
args: Vec<NestedMeta>, args: Vec<NestedMeta>,
struct_name: &Ident, struct_name: &Ident,
) -> (Literal, Literal, Literal, Literal) { ) -> AttributeConfig {
let mut class_name = None; let mut class_name = None;
let mut element_name = None; let mut element_name = None;
let mut observed_attributes = None; let mut observed_attributes = None;
let mut observed_events = None; let mut observed_events = None;
let mut base_class = None;
for arg in args { for arg in args {
if let NestedMeta::Meta(Meta::NameValue(nv)) = arg { if let NestedMeta::Meta(Meta::NameValue(nv)) = arg {
if nv.path.is_ident("class_name") { if nv.path.is_ident("class_name") {
@ -59,6 +68,10 @@ fn get_class_and_element_names(
if let Lit::Str(nm) = nv.lit { if let Lit::Str(nm) = nv.lit {
observed_events = Some(nm); observed_events = Some(nm);
} }
} else if nv.path.is_ident("base_clas") {
if let Lit::Str(nm) = nv.lit {
base_class = Some(nm);
}
} }
} }
} }
@ -74,6 +87,7 @@ fn get_class_and_element_names(
LitStr::new(&class_kebab, Span::call_site()).token() LitStr::new(&class_kebab, Span::call_site()).token()
} }
}; };
let base_class = base_class.unwrap_or_else(|| LitStr::new("HTMLElement", Span::call_site())).token();
let observed_attributes = observed_attributes let observed_attributes = observed_attributes
.map(|n| n.token()) .map(|n| n.token())
@ -81,7 +95,13 @@ fn get_class_and_element_names(
let observed_events = observed_events let observed_events = observed_events
.map(|n| n.token()) .map(|n| n.token())
.unwrap_or_else(|| LitStr::new("[]", Span::call_site()).token()); .unwrap_or_else(|| LitStr::new("[]", Span::call_site()).token());
(class_name, element_name, observed_attributes, observed_events) AttributeConfig {
class_name,
element_name,
observed_attributes,
observed_events,
base_class,
}
} }
fn expand_component_def( fn expand_component_def(
@ -107,9 +127,15 @@ fn expand_component_def(
fn expand_wc_struct_trait_shim( fn expand_wc_struct_trait_shim(
struct_name: &Ident, struct_name: &Ident,
once_name: &Ident, once_name: &Ident,
observed_attrs: Literal, config: AttributeConfig,
observed_events: Literal,
) -> syn::ItemImpl { ) -> syn::ItemImpl {
let AttributeConfig {
class_name: _,
element_name: _,
observed_attributes,
observed_events,
base_class,
} = config;
let trait_path = expand_crate_ref("wasm-web-component", parse_quote!(WebComponentDef)); let trait_path = expand_crate_ref("wasm-web-component", parse_quote!(WebComponentDef));
let handle_path = expand_crate_ref("wasm-web-component", parse_quote!(WebComponentHandle)); let handle_path = expand_crate_ref("wasm-web-component", parse_quote!(WebComponentHandle));
parse_quote! { parse_quote! {
@ -139,7 +165,7 @@ fn expand_wc_struct_trait_shim(
return Err("Custom Element has already been defined".into()); return Err("Custom Element has already been defined".into());
} }
let body = format!( let body = format!(
"class {name} extends HTMLElement {{ "class {name} extends {base_class} {{
constructor() {{ constructor() {{
super(); super();
this._impl = impl(); this._impl = impl();
@ -189,8 +215,9 @@ var element = customElements.get(\"{element_name}\");
return element;", return element;",
name = Self::class_name(), name = Self::class_name(),
element_name = Self::element_name(), element_name = Self::element_name(),
observed_attributes = #observed_attrs, observed_attributes = #observed_attributes,
observed_events = #observed_events, observed_events = #observed_events,
base_class = #base_class,
); );
let fun = js_sys::Function::new_with_args("impl", &body); let fun = js_sys::Function::new_with_args("impl", &body);
let f: Box<dyn FnMut() -> Self> = Box::new(|| { let f: Box<dyn FnMut() -> Self> = Box::new(|| {
@ -295,19 +322,16 @@ fn expand_binding(struct_name: &Ident) -> syn::ItemImpl {
fn expand_web_component_struct( fn expand_web_component_struct(
item_struct: ItemStruct, item_struct: ItemStruct,
class_name: Literal, config: AttributeConfig,
element_name: Literal,
observed_attributes: Literal,
observed_events: Literal,
) -> TokenStream { ) -> TokenStream {
let struct_name = item_struct.ident.clone(); let struct_name = item_struct.ident.clone();
let struct_once_name = Ident::new( let struct_once_name = Ident::new(
&(struct_name.to_string().to_snake_case().to_uppercase() + "_ONCE"), &(struct_name.to_string().to_snake_case().to_uppercase() + "_ONCE"),
Span::call_site(), Span::call_site(),
); );
let component_def = expand_component_def(&struct_name, &class_name, &element_name); let component_def = expand_component_def(&struct_name, &config.class_name, &config.element_name);
let non_wasm_impl = let non_wasm_impl =
expand_wc_struct_trait_shim(&struct_name, &struct_once_name, observed_attributes, observed_events); expand_wc_struct_trait_shim(&struct_name, &struct_once_name, config);
let wasm_shim = expand_wasm_shim(&struct_name); let wasm_shim = expand_wasm_shim(&struct_name);
let binding_trait = expand_binding(&struct_name); let binding_trait = expand_binding(&struct_name);
let expanded = quote! { let expanded = quote! {
@ -369,10 +393,10 @@ pub fn web_component(attr: TokenStream, item: TokenStream) -> TokenStream {
let args = parse_macro_input!(attr as AttributeArgs); let args = parse_macro_input!(attr as AttributeArgs);
let item_struct = parse_macro_input!(item as ItemStruct); let item_struct = parse_macro_input!(item as ItemStruct);
let (class_name, element_name, observed_attributes, observed_events) = let config =
get_class_and_element_names(args, &item_struct.ident); get_class_and_element_names(args, &item_struct.ident);
expand_web_component_struct(item_struct, class_name, element_name, observed_attributes, observed_events) expand_web_component_struct(item_struct, config)
} }
/// Creates the neccessary Rust and Javascript shims for rendering an HtmlTemplateElement /// Creates the neccessary Rust and Javascript shims for rendering an HtmlTemplateElement

View File

@ -25,12 +25,17 @@ use web_sys::{window, Element, Event, HtmlElement, Window};
/// ///
/// ## Example /// ## Example
/// ///
/// ```rust /// ```ignore
/// use web_sys::*;
/// use wasm_bindgen::*;
/// use wasm_web_component::{web_component, WebComponent, WebComponentHandle, WebComponentDef, WebComponentBinding};
///
/// #[web_component( /// #[web_component(
/// class_name = "MyElement", /// class_name = "MyElement",
/// element_name = "my-element", /// element_name = "my-element",
/// observed_attrs = "['class']" /// observed_attrs = "['class']",
/// observed_events = "['click']" /// observed_events = "['click']",
/// base_class = "HTMLElement"
/// )] /// )]
/// pub struct MyElementImpl {} /// pub struct MyElementImpl {}
/// ///
@ -69,7 +74,7 @@ use web_sys::{window, Element, Event, HtmlElement, Window};
/// element.append_child(&node).unwrap(); /// element.append_child(&node).unwrap();
/// } /// }
/// ///
/// fn handle_event(&self, element: &HtmlElement, event: &Event)) { /// fn handle_event(&self, element: &HtmlElement, event: &Event) {
/// // handle this event /// // handle this event
/// } /// }
/// } /// }
@ -98,7 +103,10 @@ pub use wasm_web_component_macros::web_component;
/// if the template has not been defined yet `None` will get returned. /// if the template has not been defined yet `None` will get returned.
/// ///
/// ## Example usage /// ## Example usage
/// ```rust /// ```ignore
/// use wasm_web_component::*;
/// use wasm_bindgen::*;
/// # #[cfg(feature = "HtmlTemplateElement")]
/// #[template_element] /// #[template_element]
/// pub struct MyTemplate (); /// pub struct MyTemplate ();
/// impl TemplateElementRender for MyTemplate { /// impl TemplateElementRender for MyTemplate {