#![doc = include_str!("../README.md")]
#![allow(clippy::multiple_crate_versions)]
use std::cell::RefCell;
use std::rc::Rc;
use dioxus::dioxus_core::Element;
use dioxus::hooks::UnboundedSender;
use futures_channel::oneshot;
use wasm_bindgen::prelude::*;
use web_sys::HtmlElement;
use crate::rust_component::RustComponent;
pub use dioxus_web_component_macro::web_component;
mod event;
pub use self::event::*;
mod style;
pub use self::style::*;
mod rust_component;
pub use futures_util::StreamExt;
#[derive(Debug)]
#[non_exhaustive]
pub enum Message {
SetAttribute {
name: String,
value: Option<String>,
},
Get {
name: String,
tx: oneshot::Sender<JsValue>,
},
Set {
name: String,
value: JsValue,
},
}
#[derive(Clone)]
pub struct Shared {
attributes: Vec<String>,
event_target: web_sys::HtmlElement,
tx: Rc<RefCell<Option<UnboundedSender<Message>>>>,
}
impl Shared {
#[must_use]
pub fn event_target(&self) -> HtmlElement {
self.event_target.clone()
}
pub fn set_tx(&mut self, tx: UnboundedSender<Message>) {
for attr in &self.attributes {
let Some(value) = self.event_target.get_attribute(attr) else {
continue;
};
let _ = tx.unbounded_send(Message::SetAttribute {
name: attr.to_string(),
value: Some(value),
});
}
let mut cell = self.tx.borrow_mut();
*cell = Some(tx);
}
}
pub trait DioxusWebComponent {
fn set_attribute(&mut self, attribute: &str, value: Option<String>) {
let _ = value;
let _ = attribute;
}
fn set_property(&mut self, property: &str, value: JsValue) {
let _ = value;
let _ = property;
}
fn get_property(&mut self, property: &str) -> JsValue {
let _ = property;
JsValue::undefined()
}
fn handle_message(&mut self, msg: Message) {
match msg {
Message::SetAttribute { name, value } => self.set_attribute(&name, value),
Message::Get { name, tx } => {
let value = self.get_property(&name);
let _ = tx.send(value);
}
Message::Set { name, value } => self.set_property(&name, value),
}
}
}
#[wasm_bindgen(skip_typescript)]
#[derive(Debug, Clone)]
pub struct Property {
name: String,
readonly: bool,
}
impl Property {
pub fn new(name: impl Into<String>, readonly: bool) -> Self {
let name = name.into();
Self { name, readonly }
}
}
#[wasm_bindgen]
impl Property {
#[wasm_bindgen(getter)]
#[must_use]
pub fn name(&self) -> String {
self.name.clone()
}
#[wasm_bindgen(getter)]
#[must_use]
pub fn readonly(&self) -> bool {
self.readonly
}
}
pub fn register_dioxus_web_component(
custom_tag: &str,
attributes: Vec<String>,
properties: Vec<Property>,
style: InjectedStyle,
dx_el_builder: fn() -> Element,
) {
let rust_component = RustComponent {
attributes,
properties,
style,
dx_el_builder,
};
register_web_component(custom_tag, rust_component);
}
#[wasm_bindgen(module = "/src/shim.js")]
extern "C" {
#[allow(unsafe_code)]
fn register_web_component(custom_tag: &str, rust_component: RustComponent);
}