perspective_viewer/utils/
custom_element.rs

1// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2// ┃ ██████ ██████ ██████       █      █      █      █      █ █▄  ▀███ █       ┃
3// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█  ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄  ▀█ █ ▀▀▀▀▀ ┃
4// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄   █ ▄▄▄▄▄ ┃
5// ┃ █      ██████ █  ▀█▄       █ ██████      █      ███▌▐███ ███████▄ █       ┃
6// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7// ┃ Copyright (c) 2017, the Perspective Authors.                              ┃
8// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9// ┃ This file is part of the Perspective library, distributed under the terms ┃
10// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
13use wasm_bindgen::prelude::*;
14
15pub trait CustomElementMetadata {
16    const CUSTOM_ELEMENT_NAME: &'static str;
17    const STATICS: &'static [&'static str] = [].as_slice();
18    const TYPE_NAME: &'static str = std::any::type_name::<Self>();
19
20    fn struct_name() -> &'static str {
21        match &Self::TYPE_NAME.rfind(':') {
22            Some(pos) => &Self::TYPE_NAME[pos + 1..],
23            None => Self::TYPE_NAME,
24        }
25    }
26}
27
28#[wasm_bindgen(inline_js = r#"
29    export function bootstrap(psp, name, clsname, statics) {
30        const cls = psp[clsname];
31        const proto = cls.prototype;
32        class x extends HTMLElement {
33            constructor() {
34                super();
35                this._instance = new cls(this);
36            }
37        }
38
39        const names = Object.getOwnPropertyNames(proto);
40        for (const key of names) {
41            if ('get' in Object.getOwnPropertyDescriptor(proto, key)) {
42                Object.defineProperty(x.prototype, key, {
43                    get: function() {
44                        return this._instance[key];
45                    }
46                });
47            } else {
48                Object.defineProperty(x.prototype, key, {
49                    value: function(...args) {
50                        return this._instance[key].call(this._instance, ...args);
51                    }
52                });
53            }
54        }
55
56        for (const key of statics) {
57            Object.defineProperty(x, key, {
58                value: function(...args) {
59                    return psp[key].call(psp, ...args);
60                }
61            });
62        }
63
64        Object.defineProperty(x, '__wasm_module__', {
65            get() {
66                return psp;
67            },
68        });
69
70        customElements.define(name, x);
71    }
72"#)]
73extern "C" {
74    #[wasm_bindgen(js_name = "bootstrap")]
75    fn js_bootstrap(psp: &JsValue, name: &str, cls: &str, statics: js_sys::Array) -> JsValue;
76}
77
78pub fn define_web_component<T: CustomElementMetadata>(module: &JsValue) {
79    js_bootstrap(
80        module,
81        T::CUSTOM_ELEMENT_NAME,
82        T::struct_name(),
83        T::STATICS.iter().cloned().map(JsValue::from).collect(),
84    );
85}