Crates.io | custom-elements |
lib.rs | custom-elements |
version | 0.2.1 |
source | src |
created_at | 2021-08-20 01:05:23.055359 |
updated_at | 2024-01-27 14:00:21.814899 |
description | A CustomElement trait to create Rust/WASM Web Components/Custom Elements easily without writing any JavaScript. |
homepage | https://github.com/gbj/custom-elements |
repository | https://github.com/gbj/custom-elements |
max_upload_size | |
id | 439807 |
size | 20,381 |
A framework-agnostic CustomElement trait to create Rust/WASM Web Components/Custom Elements easily without writing any JavaScript.
The Web Components standard creates a browser feature that allows you to create reusable components, called Custom Elements. (People often use the label “Web Components” when they mean Custom Elements in particular; the Web Components standard also includes Shadow DOM and HTML Templates.)
While web_sys
exposes the browser’s CustomElementRegistry interface, it can be hard to use. Creating a Custom Element requires calling customElements.define()
and passing it an ES2015 class that extends HTMLElement
, which is not currently possible to do directly from Rust.
This crate provides a CustomElement
trait that, when implemented, allows you to encapsulate any Rust structure as a reusable web component without writing any JavaScript. In theory it should be usable with any Rust front-end framework; the examples
directory contains examples for Yew and for a vanilla Rust/WASM component.
impl CustomElement for MyWebComponent {
fn inject_children(&mut self, this: &HtmlElement) {
inject_style(&this, "p { color: green; }");
let node = self.view();
this.append_child(&node).unwrap_throw();
}
fn observed_attributes() -> &'static [&'static str] {
&["name"]
}
fn attribute_changed_callback(
&mut self,
_this: &HtmlElement,
name: String,
_old_value: Option<String>,
new_value: Option<String>,
) {
if name == "name" {
/* do something... */
}
}
fn connected_callback(&mut self, _this: &HtmlElement) {
log("connected");
}
fn disconnected_callback(&mut self, _this: &HtmlElement) {
log("disconnected");
}
fn adopted_callback(&mut self, _this: &HtmlElement) {
log("adopted");
}
}
#[wasm_bindgen]
pub fn define_custom_elements() {
MyWebComponent::define("my-component");
}
By default, these custom elements use the Shadow DOM (in “open” mode) to encapsulate the styles and content of the element. You can override that choice simply by implementing the shadow
method and returning false
:
fn shadow() -> bool {
false
}
You can implement each of the custom element’s lifecycle callbacks. Each of the callbacks is passed both the component for which the trait is being implemented, and the HtmlElement
of the custom element.
fn connected_callback(&mut self, this: &HtmlElement) {
log("connected");
}
fn disconnected_callback(&mut self, this: &HtmlElement) {
log("disconnected");
}
fn adopted_callback(&mut self, this: &HtmlElement) {
log("adopted");
}
fn attribute_changed_callback(
&mut self,
this: &HtmlElement,
name: String,
old_value: Option<String>,
new_value: Option<String>,
) {
if name == "name" {
// do something
}
}
The minimum needed to implement CustomElement
is some way to inject children into the custom element. It’s also generally helpful to have it respond to changes in its attributes via the attribute_changed_callback
. Depending on the framework, these may be more or less difficult to accomplish; in particular, for Elm-inspired frameworks you may need to create a wrapper that owns some way of updating the app’s state.
See the Yew example for an example of how to work with a framework’s API.
Custom elements can either be autonomous (<my-component></my-component>
) or customized built-in elements (<p is="my-paragraph-component"></p>). This crate offers support for creating customized built-in elements via the
superclass` method.
This is a fairly minimal wrapper for the Custom Elements API. The following MDN sources should give you more than enough information to start creating custom elements:
The examples use wasm-pack
and a simple Python server. You should be able to run them with a simple
./build.sh && ./runserver