mirror of
https://github.com/zaphar/wasm-web-components.git
synced 2025-07-21 19:40:30 -04:00
Add an api for TemplateElement definition.
This commit is contained in:
parent
f5791b9355
commit
e71a28b4e3
@ -17,3 +17,6 @@ str_inflector = "0.12.0"
|
||||
[dependencies.syn]
|
||||
version = "1.0.101"
|
||||
features = ["full"]
|
||||
|
||||
[features]
|
||||
HtmlTemplateElement = []
|
||||
|
@ -96,7 +96,11 @@ fn expand_component_def(
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_struct_trait_shim(struct_name: &Ident, once_name: &Ident, observed_attrs: Literal) -> syn::ItemImpl {
|
||||
fn expand_wc_struct_trait_shim(
|
||||
struct_name: &Ident,
|
||||
once_name: &Ident,
|
||||
observed_attrs: Literal,
|
||||
) -> syn::ItemImpl {
|
||||
let trait_path = expand_crate_ref("wasm-web-component", parse_quote!(WebComponentDef));
|
||||
let handle_path = expand_crate_ref("wasm-web-component", parse_quote!(WebComponentHandle));
|
||||
parse_quote! {
|
||||
@ -115,7 +119,7 @@ fn expand_struct_trait_shim(struct_name: &Ident, once_name: &Ident, observed_att
|
||||
let _ = Self::define();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
#[doc = "Defines this web component element if not defined already otherwise returns an error."]
|
||||
pub fn define() -> std::result::Result<#handle_path, wasm_bindgen::JsValue> {
|
||||
use wasm_bindgen::JsCast;
|
||||
@ -239,16 +243,20 @@ fn expand_binding(struct_name: &Ident) -> syn::ItemImpl {
|
||||
)
|
||||
}
|
||||
|
||||
fn expand_struct(
|
||||
fn expand_web_component_struct(
|
||||
item_struct: ItemStruct,
|
||||
class_name: Literal,
|
||||
element_name: Literal,
|
||||
observed_attributes: Literal,
|
||||
) -> TokenStream {
|
||||
let struct_name = item_struct.ident.clone();
|
||||
let struct_once_name = Ident::new(&(struct_name.to_string().to_snake_case().to_uppercase() + "_ONCE"), Span::call_site());
|
||||
let struct_once_name = Ident::new(
|
||||
&(struct_name.to_string().to_snake_case().to_uppercase() + "_ONCE"),
|
||||
Span::call_site(),
|
||||
);
|
||||
let component_def = expand_component_def(&struct_name, &class_name, &element_name);
|
||||
let non_wasm_impl = expand_struct_trait_shim(&struct_name, &struct_once_name, observed_attributes);
|
||||
let non_wasm_impl =
|
||||
expand_wc_struct_trait_shim(&struct_name, &struct_once_name, observed_attributes);
|
||||
let wasm_shim = expand_wasm_shim(&struct_name);
|
||||
let binding_trait = expand_binding(&struct_name);
|
||||
let expanded = quote! {
|
||||
@ -268,6 +276,36 @@ fn expand_struct(
|
||||
TokenStream::from(expanded)
|
||||
}
|
||||
|
||||
#[cfg(feature = "HtmlTemplateElement")]
|
||||
fn expand_template_struct(item_struct: ItemStruct) -> TokenStream {
|
||||
let struct_name = item_struct.ident.clone();
|
||||
let struct_once_name = Ident::new(
|
||||
&(struct_name.to_string().to_snake_case().to_uppercase() + "_ONCE"),
|
||||
Span::call_site(),
|
||||
);
|
||||
let trait_path = expand_crate_ref("wasm-web-component", parse_quote!(TemplateElement));
|
||||
let expanded = quote! {
|
||||
use std::sync::Once;
|
||||
use web_sys::Node;
|
||||
static #struct_once_name: Once = Once::new();
|
||||
#item_struct
|
||||
impl #trait_path for #struct_name {}
|
||||
impl #struct_name {
|
||||
#[doc = "Defines this HtmlTemplateElement and adds it to the document exactly once. Subsequent calls are noops."]
|
||||
pub fn define_once() { // TODO(jwall): Should this return the element?
|
||||
#struct_once_name.call_once(|| {
|
||||
let template_element = Self::render();
|
||||
let body = web_sys::window().expect("Failed to get window")
|
||||
.document().expect("Failed to get window document").
|
||||
body().expect("Failed to get document body");
|
||||
body.append_child(template_element.as_ref()).expect("Failed to add template element to document");
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
TokenStream::from(expanded)
|
||||
}
|
||||
|
||||
/// Creates the necessary Rust and Javascript shims for a Web Component.
|
||||
#[proc_macro_attribute]
|
||||
pub fn web_component(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
@ -279,5 +317,13 @@ pub fn web_component(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let (class_name, element_name, observed_attributes) =
|
||||
get_class_and_element_names(args, &item_struct.ident);
|
||||
|
||||
expand_struct(item_struct, class_name, element_name, observed_attributes)
|
||||
expand_web_component_struct(item_struct, class_name, element_name, observed_attributes)
|
||||
}
|
||||
|
||||
/// Creates the neccessary Rust and Javascript shims for rendering an HtmlTemplateElement
|
||||
#[cfg(feature = "HtmlTemplateElement")]
|
||||
#[proc_macro_attribute]
|
||||
pub fn template_element(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let item_struct = parse_macro_input!(item as ItemStruct);
|
||||
expand_template_struct(item_struct)
|
||||
}
|
||||
|
@ -2,15 +2,14 @@
|
||||
name = "wasm-web-component"
|
||||
version = "0.1.1"
|
||||
edition = "2021"
|
||||
author = "Jeremy Wall <jeremy@marzhillstudios.com>"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
wasm-web-component-macros = { path = "../macros" }
|
||||
[dependencies.wasm-web-component-macros]
|
||||
path = "../macros"
|
||||
|
||||
[dependencies.wasm-bindgen-test]
|
||||
version = "0.3"
|
||||
@ -26,7 +25,6 @@ version = "0.3"
|
||||
features = [
|
||||
"CustomElementRegistry",
|
||||
"Document",
|
||||
#"DocumentFragment",
|
||||
"KeyboardEvent",
|
||||
"Event",
|
||||
"EventTarget",
|
||||
@ -35,7 +33,6 @@ features = [
|
||||
"Text",
|
||||
"HtmlBaseElement",
|
||||
"HtmlElement",
|
||||
"HtmlTemplateElement",
|
||||
"HtmlSlotElement",
|
||||
"Node",
|
||||
"ShadowRoot",
|
||||
@ -44,3 +41,10 @@ features = [
|
||||
"Window",
|
||||
"console"
|
||||
]
|
||||
|
||||
[features]
|
||||
default = ["HtmlTemplateElement"]
|
||||
HtmlTemplateElement = [
|
||||
"web-sys/HtmlTemplateElement",
|
||||
"wasm-web-component-macros/HtmlTemplateElement",
|
||||
]
|
||||
|
@ -1,5 +1,7 @@
|
||||
use js_sys::Function;
|
||||
use wasm_bindgen::{convert::IntoWasmAbi, JsValue};
|
||||
#[cfg(feature = "HtmlTemplateElement")]
|
||||
use web_sys::HtmlTemplateElement;
|
||||
use web_sys::{window, Element, Event, HtmlElement, Window};
|
||||
|
||||
/// This attribute proc-macro will generate the following trait implementations
|
||||
@ -82,10 +84,22 @@ pub trait WebComponentBinding: WebComponentDef {
|
||||
}
|
||||
}
|
||||
|
||||
/// Marker trait used in the generated shims to assert that their are Rust implemtntations
|
||||
/// Marker trait used in the generated shims to assert that there are Rust implemtntations
|
||||
/// of the callback functions for the component.
|
||||
pub trait WebComponent: WebComponentBinding {}
|
||||
|
||||
/// Defines the template element rendering method.
|
||||
#[cfg(feature = "HtmlTemplateElement")]
|
||||
pub trait TemplateElementRender {
|
||||
// Creates and returns an HtmlTemplateElement.
|
||||
fn render() -> HtmlTemplateElement;
|
||||
}
|
||||
|
||||
/// Marker trait used in the generated shims to assert that there are Rust implemtntations
|
||||
/// of the rendering function for the component.
|
||||
#[cfg(feature = "HtmlTemplateElement")]
|
||||
pub trait TemplateElement: TemplateElementRender {}
|
||||
|
||||
/// A handle for your WebComponent Definition. Offers easy access to construct your
|
||||
/// element.
|
||||
pub struct WebComponentHandle {
|
||||
@ -301,4 +315,32 @@ mod tests {
|
||||
assert_eq!(ThisElement::class_name(), "ThisElement");
|
||||
assert_eq!(ThisElement::element_name(), "this-old-element");
|
||||
}
|
||||
|
||||
// TODO(jwall): Benchmarks for TemplateElements?
|
||||
#[cfg(feature = "HtmlTemplateElement")]
|
||||
#[wasm_bindgen_test]
|
||||
fn test_template_element_render_once() {
|
||||
use wasm_web_component_macros::template_element;
|
||||
|
||||
#[template_element]
|
||||
pub struct MyTemplate();
|
||||
impl TemplateElementRender for MyTemplate {
|
||||
fn render() -> HtmlTemplateElement {
|
||||
let val: JsValue = window()
|
||||
.unwrap()
|
||||
.document()
|
||||
.unwrap()
|
||||
.create_element("template")
|
||||
.unwrap()
|
||||
.into();
|
||||
let el: HtmlTemplateElement = val.into();
|
||||
el
|
||||
}
|
||||
}
|
||||
|
||||
let body = window().unwrap().document().unwrap().body().unwrap();
|
||||
assert!(!body.last_child().unwrap().has_type::<HtmlTemplateElement>());
|
||||
MyTemplate::define_once();
|
||||
assert!(body.last_child().unwrap().has_type::<HtmlTemplateElement>());
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user