use crate::{innerlude::AttributeValue, AnyEvent, ElementId, VNode}; use bumpalo::boxed::Box as BumpBox; use std::{ cell::{Cell, RefCell}, fmt::{Debug, Formatter}, }; /// An element like a "div" with children, listeners, and attributes. pub struct VElement<'a> { /// The [`ElementId`] of the VText. pub id: Cell>, /// The key of the element to be used during keyed diffing. pub key: Option<&'a str>, /// The tag name of the element. /// /// IE "div" pub tag: &'static str, /// The namespace of the VElement /// /// IE "svg" pub namespace: Option<&'static str>, /// The parent of the Element (if any). /// /// Used when bubbling events pub parent: Cell>, /// The Listeners of the VElement. pub listeners: &'a [Listener<'a>], /// The attributes of the VElement. pub attributes: &'a [Attribute<'a>], /// The children of the VElement. pub children: &'a [VNode<'a>], } impl Debug for VElement<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct("VElement") .field("tag_name", &self.tag) .field("namespace", &self.namespace) .field("key", &self.key) .field("id", &self.id) .field("parent", &self.parent) .field("listeners", &self.listeners.len()) .field("attributes", &self.attributes) .field("children", &self.children) .finish() } } /// An attribute on a DOM node, such as `id="my-thing"` or /// `href="https://example.com"`. #[derive(Clone, Debug)] pub struct Attribute<'a> { /// The name of the attribute. pub name: &'static str, /// The namespace of the attribute. /// /// Doesn't exist in the html spec. /// Used in Dioxus to denote "style" tags and other attribute groups. pub namespace: Option<&'static str>, /// An indication of we should always try and set the attribute. /// Used in controlled components to ensure changes are propagated. pub volatile: bool, /// The value of the attribute. pub value: AttributeValue<'a>, } /// An event listener. /// IE onclick, onkeydown, etc pub struct Listener<'bump> { /// The ID of the node that this listener is mounted to /// Used to generate the event listener's ID on the DOM pub mounted_node: Cell, /// The type of event to listen for. /// /// IE "click" - whatever the renderer needs to attach the listener by name. pub event: &'static str, /// The actual callback that the user specified pub(crate) callback: InternalHandler<'bump>, } pub type InternalHandler<'bump> = &'bump RefCell>>; type InternalListenerCallback<'bump> = BumpBox<'bump, dyn FnMut(AnyEvent) + 'bump>; type ExternalListenerCallback<'bump, T> = BumpBox<'bump, dyn FnMut(T) + 'bump>; /// The callback type generated by the `rsx!` macro when an `on` field is specified for components. /// /// This makes it possible to pass `move |evt| {}` style closures into components as property fields. /// /// /// # Example /// /// ```rust, ignore /// /// rsx!{ /// MyComponent { onclick: move |evt| log::info!("clicked"), } /// } /// /// #[derive(Props)] /// struct MyProps<'a> { /// onclick: EventHandler<'a, MouseEvent>, /// } /// /// fn MyComponent(cx: Scope<'a, MyProps<'a>>) -> Element { /// cx.render(rsx!{ /// button { /// onclick: move |evt| cx.props.onclick.call(evt), /// } /// }) /// } /// /// ``` pub struct EventHandler<'bump, T = ()> { /// The (optional) callback that the user specified /// Uses a `RefCell` to allow for interior mutability, and FnMut closures. pub callback: RefCell>>, } impl<'a, T> Default for EventHandler<'a, T> { fn default() -> Self { Self { callback: RefCell::new(None), } } } impl EventHandler<'_, T> { /// Call this event handler with the appropriate event type pub fn call(&self, event: T) { if let Some(callback) = self.callback.borrow_mut().as_mut() { callback(event); } } /// Forcibly drop the internal handler callback, releasing memory pub fn release(&self) { self.callback.replace(None); } }