element.rs 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. use crate::{innerlude::AttributeValue, AnyEvent, ElementId, VNode};
  2. use bumpalo::boxed::Box as BumpBox;
  3. use std::{
  4. cell::{Cell, RefCell},
  5. fmt::{Debug, Formatter},
  6. };
  7. /// An element like a "div" with children, listeners, and attributes.
  8. pub struct VElement<'a> {
  9. /// The [`ElementId`] of the VText.
  10. pub id: Cell<Option<ElementId>>,
  11. /// The key of the element to be used during keyed diffing.
  12. pub key: Option<&'a str>,
  13. /// The tag name of the element.
  14. ///
  15. /// IE "div"
  16. pub tag: &'static str,
  17. /// The namespace of the VElement
  18. ///
  19. /// IE "svg"
  20. pub namespace: Option<&'static str>,
  21. /// The parent of the Element (if any).
  22. ///
  23. /// Used when bubbling events
  24. pub parent: Cell<Option<ElementId>>,
  25. /// The Listeners of the VElement.
  26. pub listeners: &'a [Listener<'a>],
  27. /// The attributes of the VElement.
  28. pub attributes: &'a [Attribute<'a>],
  29. /// The children of the VElement.
  30. pub children: &'a [VNode<'a>],
  31. }
  32. impl Debug for VElement<'_> {
  33. fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
  34. f.debug_struct("VElement")
  35. .field("tag_name", &self.tag)
  36. .field("namespace", &self.namespace)
  37. .field("key", &self.key)
  38. .field("id", &self.id)
  39. .field("parent", &self.parent)
  40. .field("listeners", &self.listeners.len())
  41. .field("attributes", &self.attributes)
  42. .field("children", &self.children)
  43. .finish()
  44. }
  45. }
  46. /// An attribute on a DOM node, such as `id="my-thing"` or
  47. /// `href="https://example.com"`.
  48. #[derive(Clone, Debug)]
  49. pub struct Attribute<'a> {
  50. /// The name of the attribute.
  51. pub name: &'static str,
  52. /// The namespace of the attribute.
  53. ///
  54. /// Doesn't exist in the html spec.
  55. /// Used in Dioxus to denote "style" tags and other attribute groups.
  56. pub namespace: Option<&'static str>,
  57. /// An indication of we should always try and set the attribute.
  58. /// Used in controlled components to ensure changes are propagated.
  59. pub volatile: bool,
  60. /// The value of the attribute.
  61. pub value: AttributeValue<'a>,
  62. }
  63. /// An event listener.
  64. /// IE onclick, onkeydown, etc
  65. pub struct Listener<'bump> {
  66. /// The ID of the node that this listener is mounted to
  67. /// Used to generate the event listener's ID on the DOM
  68. pub mounted_node: Cell<ElementId>,
  69. /// The type of event to listen for.
  70. ///
  71. /// IE "click" - whatever the renderer needs to attach the listener by name.
  72. pub event: &'static str,
  73. /// The actual callback that the user specified
  74. pub(crate) callback: InternalHandler<'bump>,
  75. }
  76. pub type InternalHandler<'bump> = &'bump RefCell<Option<InternalListenerCallback<'bump>>>;
  77. type InternalListenerCallback<'bump> = BumpBox<'bump, dyn FnMut(AnyEvent) + 'bump>;
  78. type ExternalListenerCallback<'bump, T> = BumpBox<'bump, dyn FnMut(T) + 'bump>;
  79. /// The callback type generated by the `rsx!` macro when an `on` field is specified for components.
  80. ///
  81. /// This makes it possible to pass `move |evt| {}` style closures into components as property fields.
  82. ///
  83. ///
  84. /// # Example
  85. ///
  86. /// ```rust, ignore
  87. ///
  88. /// rsx!{
  89. /// MyComponent { onclick: move |evt| log::info!("clicked"), }
  90. /// }
  91. ///
  92. /// #[derive(Props)]
  93. /// struct MyProps<'a> {
  94. /// onclick: EventHandler<'a, MouseEvent>,
  95. /// }
  96. ///
  97. /// fn MyComponent(cx: Scope<'a, MyProps<'a>>) -> Element {
  98. /// cx.render(rsx!{
  99. /// button {
  100. /// onclick: move |evt| cx.props.onclick.call(evt),
  101. /// }
  102. /// })
  103. /// }
  104. ///
  105. /// ```
  106. pub struct EventHandler<'bump, T = ()> {
  107. /// The (optional) callback that the user specified
  108. /// Uses a `RefCell` to allow for interior mutability, and FnMut closures.
  109. pub callback: RefCell<Option<ExternalListenerCallback<'bump, T>>>,
  110. }
  111. impl<'a, T> Default for EventHandler<'a, T> {
  112. fn default() -> Self {
  113. Self {
  114. callback: RefCell::new(None),
  115. }
  116. }
  117. }
  118. impl<T> EventHandler<'_, T> {
  119. /// Call this event handler with the appropriate event type
  120. pub fn call(&self, event: T) {
  121. if let Some(callback) = self.callback.borrow_mut().as_mut() {
  122. callback(event);
  123. }
  124. }
  125. /// Forcibly drop the internal handler callback, releasing memory
  126. pub fn release(&self) {
  127. self.callback.replace(None);
  128. }
  129. }