custom_element/
create_custom_element.rs

1use js_sys::{Array, Function};
2use wasm_bindgen::prelude::*;
3
4use crate::{constructors::HTML_ELEMENT_CONSTRUCTOR, Bridge, CustomElement, GeneratedConstructor};
5
6type MakeStruct = Closure<dyn FnMut(JsValue, Array) -> JsValue>;
7
8/// Javascript shim for wrapping Rust structs
9#[wasm_bindgen(module = "/src/createCustomElement.js")]
10extern "C" {
11    #[wasm_bindgen(js_name = createCustomElement)]
12    pub(self) fn _create_custom_element(
13        make_rust_struct: &MakeStruct,
14        attributes: Vec<String>,
15        constructor: &Function,
16    ) -> Function;
17}
18
19/// Allows specifying details about how your custom element
20/// should be created. Currently, this is limited to specifying
21/// which constructor function your custom element should inherit from.
22#[derive(Copy, Clone, Eq, PartialEq, Debug)]
23pub struct CustomElementConfiguration<'a> {
24    /// Which constructor your custom element should inherit from
25    pub element_constructor: &'a Function,
26}
27
28impl Default for CustomElementConfiguration<'_> {
29    fn default() -> Self {
30        Self {
31            element_constructor: &HTML_ELEMENT_CONSTRUCTOR,
32        }
33    }
34}
35
36/// The result of creating a custom element.
37pub struct CustomElementResult {
38    /// The [`CustomElementResult`].constructor is the generated
39    /// class for your custom element.
40    pub constructor: GeneratedConstructor,
41
42    /// The returned [`CustomElementResult`].closure contains
43    /// the function you gave. It must be held in memory, or else the JS
44    /// side will error when trying to call the callback.
45    ///
46    /// To have the closure live indefinitely (probably the
47    /// behavior you want), you can just call `closure.forget()`.
48    pub closure: MakeStruct,
49}
50
51/// Create a custom element with the default configuration
52pub fn create_custom_element<
53    C: CustomElement + 'static,
54    F: FnMut(JsValue, Array) -> C + 'static,
55>(
56    create_cutom_element: F,
57    attributes: Vec<String>,
58) -> (MakeStruct, GeneratedConstructor) {
59    let config = CustomElementConfiguration::default();
60    create_custom_element_with_config(create_cutom_element, attributes, config)
61}
62
63/// Create a custom element with a custom configuration
64pub fn create_custom_element_with_config<
65    C: CustomElement + 'static,
66    F: FnMut(JsValue, Array) -> C + 'static,
67>(
68    mut create_cutom_element: F,
69    attributes: Vec<String>,
70    config: CustomElementConfiguration,
71) -> (MakeStruct, GeneratedConstructor) {
72    let closure = Closure::new(move |element, args| {
73        let bridge: Bridge = create_cutom_element(element, args).into();
74        bridge.into()
75    });
76    let class = _create_custom_element(&closure, attributes, config.element_constructor);
77    (closure, GeneratedConstructor(class))
78}