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]
|
[dependencies.syn]
|
||||||
version = "1.0.101"
|
version = "1.0.101"
|
||||||
features = ["full"]
|
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 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! {
|
||||||
@ -239,16 +243,20 @@ fn expand_binding(struct_name: &Ident) -> syn::ItemImpl {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expand_struct(
|
fn expand_web_component_struct(
|
||||||
item_struct: ItemStruct,
|
item_struct: ItemStruct,
|
||||||
class_name: Literal,
|
class_name: Literal,
|
||||||
element_name: Literal,
|
element_name: Literal,
|
||||||
observed_attributes: Literal,
|
observed_attributes: Literal,
|
||||||
) -> TokenStream {
|
) -> TokenStream {
|
||||||
let struct_name = item_struct.ident.clone();
|
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 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 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! {
|
||||||
@ -268,6 +276,36 @@ fn expand_struct(
|
|||||||
TokenStream::from(expanded)
|
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.
|
/// Creates the necessary Rust and Javascript shims for a Web Component.
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn web_component(attr: TokenStream, item: TokenStream) -> TokenStream {
|
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) =
|
let (class_name, element_name, observed_attributes) =
|
||||||
get_class_and_element_names(args, &item_struct.ident);
|
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"
|
name = "wasm-web-component"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
author = "Jeremy Wall <jeremy@marzhillstudios.com>"
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["cdylib", "rlib"]
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies.wasm-web-component-macros]
|
||||||
wasm-web-component-macros = { path = "../macros" }
|
path = "../macros"
|
||||||
|
|
||||||
[dependencies.wasm-bindgen-test]
|
[dependencies.wasm-bindgen-test]
|
||||||
version = "0.3"
|
version = "0.3"
|
||||||
@ -26,7 +25,6 @@ version = "0.3"
|
|||||||
features = [
|
features = [
|
||||||
"CustomElementRegistry",
|
"CustomElementRegistry",
|
||||||
"Document",
|
"Document",
|
||||||
#"DocumentFragment",
|
|
||||||
"KeyboardEvent",
|
"KeyboardEvent",
|
||||||
"Event",
|
"Event",
|
||||||
"EventTarget",
|
"EventTarget",
|
||||||
@ -35,7 +33,6 @@ features = [
|
|||||||
"Text",
|
"Text",
|
||||||
"HtmlBaseElement",
|
"HtmlBaseElement",
|
||||||
"HtmlElement",
|
"HtmlElement",
|
||||||
"HtmlTemplateElement",
|
|
||||||
"HtmlSlotElement",
|
"HtmlSlotElement",
|
||||||
"Node",
|
"Node",
|
||||||
"ShadowRoot",
|
"ShadowRoot",
|
||||||
@ -44,3 +41,10 @@ features = [
|
|||||||
"Window",
|
"Window",
|
||||||
"console"
|
"console"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["HtmlTemplateElement"]
|
||||||
|
HtmlTemplateElement = [
|
||||||
|
"web-sys/HtmlTemplateElement",
|
||||||
|
"wasm-web-component-macros/HtmlTemplateElement",
|
||||||
|
]
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use js_sys::Function;
|
use js_sys::Function;
|
||||||
use wasm_bindgen::{convert::IntoWasmAbi, JsValue};
|
use wasm_bindgen::{convert::IntoWasmAbi, JsValue};
|
||||||
|
#[cfg(feature = "HtmlTemplateElement")]
|
||||||
|
use web_sys::HtmlTemplateElement;
|
||||||
use web_sys::{window, Element, Event, HtmlElement, Window};
|
use web_sys::{window, Element, Event, HtmlElement, Window};
|
||||||
|
|
||||||
/// This attribute proc-macro will generate the following trait implementations
|
/// 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.
|
/// of the callback functions for the component.
|
||||||
pub trait WebComponent: WebComponentBinding {}
|
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
|
/// A handle for your WebComponent Definition. Offers easy access to construct your
|
||||||
/// element.
|
/// element.
|
||||||
pub struct WebComponentHandle {
|
pub struct WebComponentHandle {
|
||||||
@ -301,4 +315,32 @@ mod tests {
|
|||||||
assert_eq!(ThisElement::class_name(), "ThisElement");
|
assert_eq!(ThisElement::class_name(), "ThisElement");
|
||||||
assert_eq!(ThisElement::element_name(), "this-old-element");
|
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