From 3d2a1a285ecefc24060ca88aa3587cc1f5792177 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Sun, 2 Oct 2022 18:16:10 -0400 Subject: [PATCH] Initial javascript shim working --- .gitignore | 2 ++ Cargo.toml | 39 +++++++++++++++++++++++++++++ src/lib.rs | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..0d387c1 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "web-component-rs" +version = "0.1.0" +edition = "2021" +author = "Jeremy Wall " + +[lib] +crate-type = ["cdylib", "rlib"] +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies.wasm-bindgen-test] +version = "0.3" + +[dependencies.wasm-bindgen] +version = "= 0.2.81" + +[dependencies.js-sys] +version = "0.3" + +[dependencies.web-sys] +version = "0.3" +features = [ + "CustomElementRegistry", + "Document", + #"DocumentFragment", + "KeyboardEvent", + "Event", + "EventTarget", + "Element", + "HtmlBaseElement", + "HtmlElement", + "HtmlTemplateElement", + "HtmlSlotElement", + "Node", + "ShadowRoot", + "ShadowRootInit", + "ShadowRootMode", + "Window", +] \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..fdb02ef --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,73 @@ +use js_sys::Function; +use wasm_bindgen::JsValue; +use web_sys::window; + +type Result = std::result::Result; + +pub trait CustomElementImpl { + fn class_name() -> &'static str; + fn element_name() -> &'static str; +} + +pub fn define_web_component() -> Result { + let body = format!( + "class {name} extends HTMLElement {{ + constructor() {{ + super(); + }} +}} +customElements.define(\"{element_name}\", {name}); +var element = customElements.get(\"{element_name}\"); +return element;", + name = T::class_name(), + element_name = T::element_name(), + ); + let fun = Function::new_no_args(&body); + Ok(fun.call0(&window().unwrap())?) +} + +#[cfg(test)] +mod tests { + use super::*; + use wasm_bindgen::{prelude::*, JsCast}; + use wasm_bindgen_test::wasm_bindgen_test; + wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + + pub struct MyElementImpl(); + + impl CustomElementImpl for MyElementImpl { + fn class_name() -> &'static str { + "MyElement" + } + + fn element_name() -> &'static str { + "my-element" + } + } + + #[wasm_bindgen] + extern "C" { + #[wasm_bindgen(js_namespace = console, js_name = log)] + pub fn log(message: String); + #[wasm_bindgen(js_namespace = console, js_name = log)] + pub fn log_with_val(message: String, val: &JsValue); + } + + #[wasm_bindgen_test] + fn test_component_definition() { + let obj = define_web_component::().expect("Failed to define web component"); + let fun = obj.dyn_ref::().unwrap(); + assert_eq!(fun.name(), MyElementImpl::class_name()); + + let element = window() + .unwrap() + .document() + .unwrap() + .create_element(MyElementImpl::element_name()) + .unwrap(); + assert_eq!( + element.tag_name().to_uppercase(), + MyElementImpl::element_name().to_uppercase() + ); + } +}