1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
use js_ffi::*;
extern crate alloc;
use alloc::sync::Arc;
use spin::Mutex;

pub struct JSNoDrop(pub JSValue);

impl ToJSValue for JSNoDrop {
    #[inline]
    fn to_js_value(&self) -> JSValue {
        self.0
    }

    #[inline]
    fn to_js_type(&self) -> JSType {
        TYPE_OBJECT
    }
}

pub trait CustomElement {
    fn new(element: JSObject) -> Self
    where
        Self: core::marker::Sized + core::marker::Sync + core::marker::Send + 'static;
    fn register(name: &str)
    where
        Self: core::marker::Sized + core::marker::Sync + core::marker::Send + 'static,
    {
        let construct = create_callback_1(|element| {
            let el = Arc::new(Mutex::new(Self::new(JSObject(element))));
            let el1 = el.clone();
            let el2 = el.clone();
            let el3 = el.clone();
            let connect = create_callback_0(move ||{
              el1.lock().connected();
            });
            let disconnect = create_callback_0(move ||{
              el2.lock().disconnected();
            });
            let attribute_change = create_callback_3(move |name,old,new|{
              el3.lock().attribute_changed(name,old,new);
            });
            js!((e,a,b,c)=>{
              e.addHooks(a,b,c);
            }).invoke_4(JSNoDrop(element),connect,disconnect,attribute_change);
        });
        js!(
          (construct,elementName)=>{
            class GeneratedCustomElement extends HTMLElement {
              constructor() {
                  super();
                  construct(this);
              }

              connectedCallback() {
                self.connect();
              }

              disconnectedCallback() {
                self.disconnect();
              }

              attributeChangedCallback(attributeName, oldValue, newValue) {
                self.attributeChange(attributeName,oldValue,newValue)
              }

              addHooks(connect,disconnect,attributeChange){
                self.connect = connect;
                self.disconnect = disconnect;
                self.attributeChange = attributeChange;
              }
            }

            // tell the dom to associate it with an html tag name
            customElements.define(elementName, GeneratedCustomElement);
          }
        )
        .invoke_2(construct, name);
    }
    fn created(&mut self) {}
    fn connected(&mut self) {}
    fn disconnected(&mut self) {}
    fn attribute_changed(&mut self, _name: JSValue, _old_value: JSValue, _new_value: JSValue) {}
}