nodes.rs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975
  1. use crate::innerlude::VProps;
  2. use crate::{any_props::BoxedAnyProps, innerlude::ScopeState};
  3. use crate::{arena::ElementId, Element, Event};
  4. use crate::{
  5. innerlude::{ElementRef, EventHandler, MountId},
  6. properties::ComponentFunction,
  7. };
  8. use crate::{Properties, VirtualDom};
  9. use core::panic;
  10. use std::ops::Deref;
  11. use std::rc::Rc;
  12. use std::vec;
  13. use std::{
  14. any::{Any, TypeId},
  15. cell::Cell,
  16. fmt::{Arguments, Debug},
  17. };
  18. pub type TemplateId = &'static str;
  19. /// The actual state of the component's most recent computation
  20. ///
  21. /// Because Dioxus accepts components in the form of `async fn() -> Result<VNode>`, we need to support both
  22. /// sync and async versions.
  23. ///
  24. /// Dioxus will do its best to immediately resolve any async components into a regular Element, but as an implementor
  25. /// you might need to handle the case where there's no node immediately ready.
  26. pub enum RenderReturn {
  27. /// A currently-available element
  28. Ready(VNode),
  29. /// The component aborted rendering early. It might've thrown an error.
  30. ///
  31. /// In its place we've produced a placeholder to locate its spot in the dom when it recovers.
  32. Aborted(VNode),
  33. }
  34. impl Clone for RenderReturn {
  35. fn clone(&self) -> Self {
  36. match self {
  37. RenderReturn::Ready(node) => RenderReturn::Ready(node.clone_mounted()),
  38. RenderReturn::Aborted(node) => RenderReturn::Aborted(node.clone_mounted()),
  39. }
  40. }
  41. }
  42. impl Default for RenderReturn {
  43. fn default() -> Self {
  44. RenderReturn::Aborted(VNode::placeholder())
  45. }
  46. }
  47. impl Deref for RenderReturn {
  48. type Target = VNode;
  49. fn deref(&self) -> &Self::Target {
  50. match self {
  51. RenderReturn::Ready(node) | RenderReturn::Aborted(node) => node,
  52. }
  53. }
  54. }
  55. /// The information about the
  56. #[derive(Debug)]
  57. pub(crate) struct VNodeMount {
  58. /// The parent of this node
  59. pub parent: Option<ElementRef>,
  60. /// A back link to the original node
  61. pub node: VNode,
  62. /// The IDs for the roots of this template - to be used when moving the template around and removing it from
  63. /// the actual Dom
  64. pub root_ids: Box<[ElementId]>,
  65. /// The element in the DOM that each attribute is mounted to
  66. pub(crate) mounted_attributes: Box<[ElementId]>,
  67. /// For components: This is the ScopeId the component is mounted to
  68. /// For other dynamic nodes: This is element in the DOM that each dynamic node is mounted to
  69. pub(crate) mounted_dynamic_nodes: Box<[usize]>,
  70. }
  71. /// A reference to a template along with any context needed to hydrate it
  72. ///
  73. /// The dynamic parts of the template are stored separately from the static parts. This allows faster diffing by skipping
  74. /// static parts of the template.
  75. #[derive(Debug)]
  76. pub struct VNodeInner {
  77. /// The key given to the root of this template.
  78. ///
  79. /// In fragments, this is the key of the first child. In other cases, it is the key of the root.
  80. pub key: Option<String>,
  81. /// The static nodes and static descriptor of the template
  82. pub template: Cell<Template>,
  83. /// The dynamic nodes in the template
  84. pub dynamic_nodes: Box<[DynamicNode]>,
  85. /// The dynamic attribute slots in the template
  86. ///
  87. /// This is a list of positions in the template where dynamic attributes can be inserted.
  88. ///
  89. /// The inner list *must* be in the format [static named attributes, remaining dynamically named attributes].
  90. ///
  91. /// For example:
  92. /// ```rust, ignore
  93. /// div {
  94. /// class: "{class}",
  95. /// ..attrs,
  96. /// p {
  97. /// color: "{color}",
  98. /// }
  99. /// }
  100. /// ```
  101. ///
  102. /// Would be represented as:
  103. /// ```rust, ignore
  104. /// [
  105. /// [class, every attribute in attrs sorted by name], // Slot 0 in the template
  106. /// [color], // Slot 1 in the template
  107. /// ]
  108. /// ```
  109. pub dynamic_attrs: Box<[Box<[Attribute]>]>,
  110. }
  111. /// A reference to a template along with any context needed to hydrate it
  112. ///
  113. /// The dynamic parts of the template are stored separately from the static parts. This allows faster diffing by skipping
  114. /// static parts of the template.
  115. #[derive(Debug)]
  116. pub struct VNode {
  117. vnode: Rc<VNodeInner>,
  118. /// The mount information for this template
  119. pub(crate) mount: Cell<MountId>,
  120. }
  121. impl Clone for VNode {
  122. fn clone(&self) -> Self {
  123. Self {
  124. vnode: self.vnode.clone(),
  125. mount: Default::default(),
  126. }
  127. }
  128. }
  129. impl PartialEq for VNode {
  130. fn eq(&self, other: &Self) -> bool {
  131. Rc::ptr_eq(&self.vnode, &other.vnode)
  132. }
  133. }
  134. impl Deref for VNode {
  135. type Target = VNodeInner;
  136. fn deref(&self) -> &Self::Target {
  137. &self.vnode
  138. }
  139. }
  140. impl VNode {
  141. /// Clone the element while retaining the mount information of the node
  142. pub(crate) fn clone_mounted(&self) -> Self {
  143. Self {
  144. vnode: self.vnode.clone(),
  145. mount: self.mount.clone(),
  146. }
  147. }
  148. /// Create a template with no nodes that will be skipped over during diffing
  149. pub fn empty() -> Element {
  150. Some(Self {
  151. vnode: Rc::new(VNodeInner {
  152. key: None,
  153. dynamic_nodes: Box::new([]),
  154. dynamic_attrs: Box::new([]),
  155. template: Cell::new(Template {
  156. name: "packages/core/nodes.rs:180:0:0",
  157. roots: &[],
  158. node_paths: &[],
  159. attr_paths: &[],
  160. }),
  161. }),
  162. mount: Default::default(),
  163. })
  164. }
  165. /// Create a template with a single placeholder node
  166. pub fn placeholder() -> Self {
  167. Self {
  168. vnode: Rc::new(VNodeInner {
  169. key: None,
  170. dynamic_nodes: Box::new([DynamicNode::Placeholder(Default::default())]),
  171. dynamic_attrs: Box::new([]),
  172. template: Cell::new(Template {
  173. name: "packages/core/nodes.rs:198:0:0",
  174. roots: &[TemplateNode::Dynamic { id: 0 }],
  175. node_paths: &[&[]],
  176. attr_paths: &[],
  177. }),
  178. }),
  179. mount: Default::default(),
  180. }
  181. }
  182. /// Create a new VNode
  183. pub fn new(
  184. key: Option<String>,
  185. template: Template,
  186. dynamic_nodes: Box<[DynamicNode]>,
  187. dynamic_attrs: Box<[Box<[Attribute]>]>,
  188. ) -> Self {
  189. Self {
  190. vnode: Rc::new(VNodeInner {
  191. key,
  192. template: Cell::new(template),
  193. dynamic_nodes,
  194. dynamic_attrs,
  195. }),
  196. mount: Default::default(),
  197. }
  198. }
  199. /// Load a dynamic root at the given index
  200. ///
  201. /// Returns [`None`] if the root is actually a static node (Element/Text)
  202. pub fn dynamic_root(&self, idx: usize) -> Option<&DynamicNode> {
  203. match &self.template.get().roots[idx] {
  204. TemplateNode::Element { .. } | TemplateNode::Text { text: _ } => None,
  205. TemplateNode::Dynamic { id } | TemplateNode::DynamicText { id } => {
  206. Some(&self.dynamic_nodes[*id])
  207. }
  208. }
  209. }
  210. /// Get the mounted id for a dynamic node index
  211. pub fn mounted_dynamic_node(
  212. &self,
  213. dynamic_node_idx: usize,
  214. dom: &VirtualDom,
  215. ) -> Option<ElementId> {
  216. let mount = self.mount.get().as_usize()?;
  217. match &self.dynamic_nodes[dynamic_node_idx] {
  218. DynamicNode::Text(_) | DynamicNode::Placeholder(_) => dom
  219. .mounts
  220. .get(mount)?
  221. .mounted_dynamic_nodes
  222. .get(dynamic_node_idx)
  223. .map(|id| ElementId(*id)),
  224. _ => None,
  225. }
  226. }
  227. /// Get the mounted id for a root node index
  228. pub fn mounted_root(&self, root_idx: usize, dom: &VirtualDom) -> Option<ElementId> {
  229. let mount = self.mount.get().as_usize()?;
  230. dom.mounts.get(mount)?.root_ids.get(root_idx).copied()
  231. }
  232. /// Get the mounted id for a dynamic attribute index
  233. pub fn mounted_dynamic_attribute(
  234. &self,
  235. dynamic_attribute_idx: usize,
  236. dom: &VirtualDom,
  237. ) -> Option<ElementId> {
  238. let mount = self.mount.get().as_usize()?;
  239. dom.mounts
  240. .get(mount)?
  241. .mounted_attributes
  242. .get(dynamic_attribute_idx)
  243. .copied()
  244. }
  245. }
  246. /// A static layout of a UI tree that describes a set of dynamic and static nodes.
  247. ///
  248. /// This is the core innovation in Dioxus. Most UIs are made of static nodes, yet participate in diffing like any
  249. /// dynamic node. This struct can be created at compile time. It promises that its name is unique, allow Dioxus to use
  250. /// its static description of the UI to skip immediately to the dynamic nodes during diffing.
  251. ///
  252. /// For this to work properly, the [`Template::name`] *must* be unique across your entire project. This can be done via variety of
  253. /// ways, with the suggested approach being the unique code location (file, line, col, etc).
  254. #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
  255. #[derive(Debug, Clone, Copy, PartialEq, Hash, Eq, PartialOrd, Ord)]
  256. pub struct Template {
  257. /// The name of the template. This must be unique across your entire program for template diffing to work properly
  258. ///
  259. /// If two templates have the same name, it's likely that Dioxus will panic when diffing.
  260. #[cfg_attr(
  261. feature = "serialize",
  262. serde(deserialize_with = "deserialize_string_leaky")
  263. )]
  264. pub name: &'static str,
  265. /// The list of template nodes that make up the template
  266. ///
  267. /// Unlike react, calls to `rsx!` can have multiple roots. This list supports that paradigm.
  268. #[cfg_attr(feature = "serialize", serde(deserialize_with = "deserialize_leaky"))]
  269. pub roots: &'static [TemplateNode],
  270. /// The paths of each node relative to the root of the template.
  271. ///
  272. /// These will be one segment shorter than the path sent to the renderer since those paths are relative to the
  273. /// topmost element, not the `roots` field.
  274. #[cfg_attr(
  275. feature = "serialize",
  276. serde(deserialize_with = "deserialize_bytes_leaky")
  277. )]
  278. pub node_paths: &'static [&'static [u8]],
  279. /// The paths of each dynamic attribute relative to the root of the template
  280. ///
  281. /// These will be one segment shorter than the path sent to the renderer since those paths are relative to the
  282. /// topmost element, not the `roots` field.
  283. #[cfg_attr(
  284. feature = "serialize",
  285. serde(deserialize_with = "deserialize_bytes_leaky")
  286. )]
  287. pub attr_paths: &'static [&'static [u8]],
  288. }
  289. #[cfg(feature = "serialize")]
  290. fn deserialize_string_leaky<'a, 'de, D>(deserializer: D) -> Result<&'a str, D::Error>
  291. where
  292. D: serde::Deserializer<'de>,
  293. {
  294. use serde::Deserialize;
  295. let deserialized = String::deserialize(deserializer)?;
  296. Ok(&*Box::leak(deserialized.into_boxed_str()))
  297. }
  298. #[cfg(feature = "serialize")]
  299. fn deserialize_bytes_leaky<'a, 'de, D>(deserializer: D) -> Result<&'a [&'a [u8]], D::Error>
  300. where
  301. D: serde::Deserializer<'de>,
  302. {
  303. use serde::Deserialize;
  304. let deserialized = Vec::<Vec<u8>>::deserialize(deserializer)?;
  305. let deserialized = deserialized
  306. .into_iter()
  307. .map(|v| &*Box::leak(v.into_boxed_slice()))
  308. .collect::<Vec<_>>();
  309. Ok(&*Box::leak(deserialized.into_boxed_slice()))
  310. }
  311. #[cfg(feature = "serialize")]
  312. fn deserialize_leaky<'a, 'de, T: serde::Deserialize<'de>, D>(
  313. deserializer: D,
  314. ) -> Result<&'a [T], D::Error>
  315. where
  316. T: serde::Deserialize<'de>,
  317. D: serde::Deserializer<'de>,
  318. {
  319. use serde::Deserialize;
  320. let deserialized = Box::<[T]>::deserialize(deserializer)?;
  321. Ok(&*Box::leak(deserialized))
  322. }
  323. #[cfg(feature = "serialize")]
  324. fn deserialize_option_leaky<'a, 'de, D>(deserializer: D) -> Result<Option<&'static str>, D::Error>
  325. where
  326. D: serde::Deserializer<'de>,
  327. {
  328. use serde::Deserialize;
  329. let deserialized = Option::<String>::deserialize(deserializer)?;
  330. Ok(deserialized.map(|deserialized| &*Box::leak(deserialized.into_boxed_str())))
  331. }
  332. impl Template {
  333. /// Is this template worth caching at all, since it's completely runtime?
  334. ///
  335. /// There's no point in saving templates that are completely dynamic, since they'll be recreated every time anyway.
  336. pub fn is_completely_dynamic(&self) -> bool {
  337. use TemplateNode::*;
  338. self.roots
  339. .iter()
  340. .all(|root| matches!(root, Dynamic { .. } | DynamicText { .. }))
  341. }
  342. }
  343. /// A statically known node in a layout.
  344. ///
  345. /// This can be created at compile time, saving the VirtualDom time when diffing the tree
  346. #[derive(Debug, Clone, Copy, PartialEq, Hash, Eq, PartialOrd, Ord)]
  347. #[cfg_attr(
  348. feature = "serialize",
  349. derive(serde::Serialize, serde::Deserialize),
  350. serde(tag = "type")
  351. )]
  352. pub enum TemplateNode {
  353. /// An statically known element in the dom.
  354. ///
  355. /// In HTML this would be something like `<div id="123"> </div>`
  356. Element {
  357. /// The name of the element
  358. ///
  359. /// IE for a div, it would be the string "div"
  360. tag: &'static str,
  361. /// The namespace of the element
  362. ///
  363. /// In HTML, this would be a valid URI that defines a namespace for all elements below it
  364. /// SVG is an example of this namespace
  365. #[cfg_attr(
  366. feature = "serialize",
  367. serde(deserialize_with = "deserialize_option_leaky")
  368. )]
  369. namespace: Option<&'static str>,
  370. /// A list of possibly dynamic attributes for this element
  371. ///
  372. /// An attribute on a DOM node, such as `id="my-thing"` or `href="https://example.com"`.
  373. #[cfg_attr(feature = "serialize", serde(deserialize_with = "deserialize_leaky"))]
  374. attrs: &'static [TemplateAttribute],
  375. /// A list of template nodes that define another set of template nodes
  376. #[cfg_attr(feature = "serialize", serde(deserialize_with = "deserialize_leaky"))]
  377. children: &'static [TemplateNode],
  378. },
  379. /// This template node is just a piece of static text
  380. Text {
  381. /// The actual text
  382. text: &'static str,
  383. },
  384. /// This template node is unknown, and needs to be created at runtime.
  385. Dynamic {
  386. /// The index of the dynamic node in the VNode's dynamic_nodes list
  387. id: usize,
  388. },
  389. /// This template node is known to be some text, but needs to be created at runtime
  390. ///
  391. /// This is separate from the pure Dynamic variant for various optimizations
  392. DynamicText {
  393. /// The index of the dynamic node in the VNode's dynamic_nodes list
  394. id: usize,
  395. },
  396. }
  397. impl TemplateNode {
  398. /// Try to load the dynamic node at the given index
  399. pub fn dynamic_id(&self) -> Option<usize> {
  400. use TemplateNode::*;
  401. match self {
  402. Dynamic { id } | DynamicText { id } => Some(*id),
  403. _ => None,
  404. }
  405. }
  406. }
  407. /// A node created at runtime
  408. ///
  409. /// This node's index in the DynamicNode list on VNode should match its repsective `Dynamic` index
  410. #[derive(Debug)]
  411. pub enum DynamicNode {
  412. /// A component node
  413. ///
  414. /// Most of the time, Dioxus will actually know which component this is as compile time, but the props and
  415. /// assigned scope are dynamic.
  416. ///
  417. /// The actual VComponent can be dynamic between two VNodes, though, allowing implementations to swap
  418. /// the render function at runtime
  419. Component(VComponent),
  420. /// A text node
  421. Text(VText),
  422. /// A placeholder
  423. ///
  424. /// Used by suspense when a node isn't ready and by fragments that don't render anything
  425. ///
  426. /// In code, this is just an ElementId whose initial value is set to 0 upon creation
  427. Placeholder(VPlaceholder),
  428. /// A list of VNodes.
  429. ///
  430. /// Note that this is not a list of dynamic nodes. These must be VNodes and created through conditional rendering
  431. /// or iterators.
  432. Fragment(Vec<VNode>),
  433. }
  434. impl DynamicNode {
  435. /// Convert any item that implements [`IntoDynNode`] into a [`DynamicNode`]
  436. pub fn make_node<'c, I>(into: impl IntoDynNode<I> + 'c) -> DynamicNode {
  437. into.into_dyn_node()
  438. }
  439. }
  440. impl Default for DynamicNode {
  441. fn default() -> Self {
  442. Self::Placeholder(Default::default())
  443. }
  444. }
  445. /// An instance of a child component
  446. pub struct VComponent {
  447. /// The name of this component
  448. pub name: &'static str,
  449. /// The function pointer of the component, known at compile time
  450. ///
  451. /// It is possible that components get folded at compile time, so these shouldn't be really used as a key
  452. pub(crate) render_fn: TypeId,
  453. pub(crate) props: BoxedAnyProps,
  454. }
  455. impl VComponent {
  456. /// Create a new [`VComponent`] variant
  457. pub fn new<P, M: 'static>(
  458. component: impl ComponentFunction<P, M>,
  459. props: P,
  460. fn_name: &'static str,
  461. ) -> Self
  462. where
  463. P: Properties + 'static,
  464. {
  465. let render_fn = component.id();
  466. let props = Box::new(VProps::new(
  467. component,
  468. <P as Properties>::memoize,
  469. props,
  470. fn_name,
  471. ));
  472. VComponent {
  473. name: fn_name,
  474. props,
  475. render_fn,
  476. }
  477. }
  478. /// Get the scope this node is mounted to if it's mounted
  479. ///
  480. /// This is useful for rendering nodes outside of the VirtualDom, such as in SSR
  481. ///
  482. /// Returns [`None`] if the node is not mounted
  483. pub fn mounted_scope<'a>(
  484. &self,
  485. dynamic_node_index: usize,
  486. vnode: &VNode,
  487. dom: &'a VirtualDom,
  488. ) -> Option<&'a ScopeState> {
  489. let mount = vnode.mount.get().as_usize()?;
  490. let scope_id = dom.mounts.get(mount)?.mounted_dynamic_nodes[dynamic_node_index];
  491. dom.scopes.get(scope_id)
  492. }
  493. }
  494. impl std::fmt::Debug for VComponent {
  495. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  496. f.debug_struct("VComponent")
  497. .field("name", &self.name)
  498. .finish()
  499. }
  500. }
  501. /// A text node
  502. #[derive(Clone, Debug)]
  503. pub struct VText {
  504. /// The actual text itself
  505. pub value: String,
  506. }
  507. impl VText {
  508. /// Create a new VText
  509. pub fn new(value: String) -> Self {
  510. Self { value }
  511. }
  512. }
  513. impl From<Arguments<'_>> for VText {
  514. fn from(args: Arguments) -> Self {
  515. Self::new(args.to_string())
  516. }
  517. }
  518. /// A placeholder node, used by suspense and fragments
  519. #[derive(Clone, Debug, Default)]
  520. #[non_exhaustive]
  521. pub struct VPlaceholder {}
  522. /// An attribute of the TemplateNode, created at compile time
  523. #[derive(Debug, PartialEq, Hash, Eq, PartialOrd, Ord)]
  524. #[cfg_attr(
  525. feature = "serialize",
  526. derive(serde::Serialize, serde::Deserialize),
  527. serde(tag = "type")
  528. )]
  529. pub enum TemplateAttribute {
  530. /// This attribute is entirely known at compile time, enabling
  531. Static {
  532. /// The name of this attribute.
  533. ///
  534. /// For example, the `href` attribute in `href="https://example.com"`, would have the name "href"
  535. name: &'static str,
  536. /// The value of this attribute, known at compile time
  537. ///
  538. /// Currently this only accepts &str, so values, even if they're known at compile time, are not known
  539. value: &'static str,
  540. /// The namespace of this attribute. Does not exist in the HTML spec
  541. namespace: Option<&'static str>,
  542. },
  543. /// The attribute in this position is actually determined dynamically at runtime
  544. ///
  545. /// This is the index into the dynamic_attributes field on the container VNode
  546. Dynamic {
  547. /// The index
  548. id: usize,
  549. },
  550. }
  551. /// An attribute on a DOM node, such as `id="my-thing"` or `href="https://example.com"`
  552. #[derive(Debug, Clone, PartialEq)]
  553. pub struct Attribute {
  554. /// The name of the attribute.
  555. pub name: &'static str,
  556. /// The value of the attribute
  557. pub value: AttributeValue,
  558. /// The namespace of the attribute.
  559. ///
  560. /// Doesn’t exist in the html spec. Used in Dioxus to denote “style” tags and other attribute groups.
  561. pub namespace: Option<&'static str>,
  562. /// An indication of we should always try and set the attribute. Used in controlled components to ensure changes are propagated
  563. pub volatile: bool,
  564. }
  565. impl Attribute {
  566. /// Create a new [`Attribute`] from a name, value, namespace, and volatile bool
  567. ///
  568. /// "Volatile" referes to whether or not Dioxus should always override the value. This helps prevent the UI in
  569. /// some renderers stay in sync with the VirtualDom's understanding of the world
  570. pub fn new(
  571. name: &'static str,
  572. value: impl IntoAttributeValue,
  573. namespace: Option<&'static str>,
  574. volatile: bool,
  575. ) -> Attribute {
  576. Attribute {
  577. name,
  578. namespace,
  579. volatile,
  580. value: value.into_value(),
  581. }
  582. }
  583. }
  584. /// Any of the built-in values that the Dioxus VirtualDom supports as dynamic attributes on elements
  585. ///
  586. /// These are built-in to be faster during the diffing process. To use a custom value, use the [`AttributeValue::Any`]
  587. /// variant.
  588. pub enum AttributeValue {
  589. /// Text attribute
  590. Text(String),
  591. /// A float
  592. Float(f64),
  593. /// Signed integer
  594. Int(i64),
  595. /// Boolean
  596. Bool(bool),
  597. /// A listener, like "onclick"
  598. Listener(ListenerCb),
  599. /// An arbitrary value that implements PartialEq and is static
  600. Any(Box<dyn AnyValue>),
  601. /// A "none" value, resulting in the removal of an attribute from the dom
  602. None,
  603. }
  604. impl AttributeValue {
  605. /// Create a new [`AttributeValue`] with the listener variant from a callback
  606. ///
  607. /// The callback must be confined to the lifetime of the ScopeState
  608. pub fn listener<T: 'static>(mut callback: impl FnMut(Event<T>) + 'static) -> AttributeValue {
  609. AttributeValue::Listener(EventHandler::new(move |event: Event<dyn Any>| {
  610. let data = event.data.downcast::<T>().unwrap();
  611. // if let Ok(data) = event.data.downcast::<T>() {
  612. callback(Event {
  613. propagates: event.propagates,
  614. data,
  615. });
  616. // }
  617. }))
  618. }
  619. /// Create a new [`AttributeValue`] with a value that implements [`AnyValue`]
  620. pub fn any_value<T: AnyValue>(value: T) -> AttributeValue {
  621. AttributeValue::Any(Box::new(value))
  622. }
  623. }
  624. pub type ListenerCb = EventHandler<Event<dyn Any>>;
  625. impl std::fmt::Debug for AttributeValue {
  626. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  627. match self {
  628. Self::Text(arg0) => f.debug_tuple("Text").field(arg0).finish(),
  629. Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
  630. Self::Int(arg0) => f.debug_tuple("Int").field(arg0).finish(),
  631. Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
  632. Self::Listener(_) => f.debug_tuple("Listener").finish(),
  633. Self::Any(_) => f.debug_tuple("Any").finish(),
  634. Self::None => write!(f, "None"),
  635. }
  636. }
  637. }
  638. impl PartialEq for AttributeValue {
  639. fn eq(&self, other: &Self) -> bool {
  640. match (self, other) {
  641. (Self::Text(l0), Self::Text(r0)) => l0 == r0,
  642. (Self::Float(l0), Self::Float(r0)) => l0 == r0,
  643. (Self::Int(l0), Self::Int(r0)) => l0 == r0,
  644. (Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
  645. (Self::Listener(_), Self::Listener(_)) => true,
  646. (Self::Any(l0), Self::Any(r0)) => l0.as_ref().any_cmp(r0.as_ref()),
  647. (Self::None, Self::None) => true,
  648. _ => false,
  649. }
  650. }
  651. }
  652. impl Clone for AttributeValue {
  653. fn clone(&self) -> Self {
  654. match self {
  655. Self::Text(arg0) => Self::Text(arg0.clone()),
  656. Self::Float(arg0) => Self::Float(*arg0),
  657. Self::Int(arg0) => Self::Int(*arg0),
  658. Self::Bool(arg0) => Self::Bool(*arg0),
  659. Self::Listener(_) | Self::Any(_) => panic!("Cannot clone listener or any value"),
  660. Self::None => Self::None,
  661. }
  662. }
  663. }
  664. #[doc(hidden)]
  665. pub trait AnyValue: 'static {
  666. fn any_cmp(&self, other: &dyn AnyValue) -> bool;
  667. fn as_any(&self) -> &dyn Any;
  668. fn type_id(&self) -> TypeId {
  669. self.as_any().type_id()
  670. }
  671. }
  672. impl<T: Any + PartialEq + 'static> AnyValue for T {
  673. fn any_cmp(&self, other: &dyn AnyValue) -> bool {
  674. if let Some(other) = other.as_any().downcast_ref() {
  675. self == other
  676. } else {
  677. false
  678. }
  679. }
  680. fn as_any(&self) -> &dyn Any {
  681. self
  682. }
  683. }
  684. /// A trait that allows various items to be converted into a dynamic node for the rsx macro
  685. pub trait IntoDynNode<A = ()> {
  686. /// Consume this item along with a scopestate and produce a DynamicNode
  687. ///
  688. /// You can use the bump alloactor of the scopestate to creat the dynamic node
  689. fn into_dyn_node(self) -> DynamicNode;
  690. }
  691. impl IntoDynNode for () {
  692. fn into_dyn_node(self) -> DynamicNode {
  693. DynamicNode::default()
  694. }
  695. }
  696. impl IntoDynNode for VNode {
  697. fn into_dyn_node(self) -> DynamicNode {
  698. DynamicNode::Fragment(vec![self])
  699. }
  700. }
  701. impl IntoDynNode for DynamicNode {
  702. fn into_dyn_node(self) -> DynamicNode {
  703. self
  704. }
  705. }
  706. impl<T: IntoDynNode> IntoDynNode for Option<T> {
  707. fn into_dyn_node(self) -> DynamicNode {
  708. match self {
  709. Some(val) => val.into_dyn_node(),
  710. None => DynamicNode::default(),
  711. }
  712. }
  713. }
  714. impl IntoDynNode for &Element {
  715. fn into_dyn_node(self) -> DynamicNode {
  716. match self.as_ref() {
  717. Some(val) => val.clone().into_dyn_node(),
  718. _ => DynamicNode::default(),
  719. }
  720. }
  721. }
  722. impl IntoDynNode for &str {
  723. fn into_dyn_node(self) -> DynamicNode {
  724. DynamicNode::Text(VText {
  725. value: self.to_string(),
  726. })
  727. }
  728. }
  729. impl IntoDynNode for String {
  730. fn into_dyn_node(self) -> DynamicNode {
  731. DynamicNode::Text(VText { value: self })
  732. }
  733. }
  734. impl IntoDynNode for Arguments<'_> {
  735. fn into_dyn_node(self) -> DynamicNode {
  736. DynamicNode::Text(VText {
  737. value: self.to_string(),
  738. })
  739. }
  740. }
  741. impl IntoDynNode for &VNode {
  742. fn into_dyn_node(self) -> DynamicNode {
  743. DynamicNode::Fragment(vec![self.clone()])
  744. }
  745. }
  746. pub trait IntoVNode {
  747. fn into_vnode(self) -> VNode;
  748. }
  749. impl IntoVNode for VNode {
  750. fn into_vnode(self) -> VNode {
  751. self
  752. }
  753. }
  754. impl IntoVNode for &VNode {
  755. fn into_vnode(self) -> VNode {
  756. self.clone()
  757. }
  758. }
  759. impl IntoVNode for Element {
  760. fn into_vnode(self) -> VNode {
  761. match self {
  762. Some(val) => val.into_vnode(),
  763. _ => VNode::empty().unwrap(),
  764. }
  765. }
  766. }
  767. impl IntoVNode for &Element {
  768. fn into_vnode(self) -> VNode {
  769. match self {
  770. Some(val) => val.into_vnode(),
  771. _ => VNode::empty().unwrap(),
  772. }
  773. }
  774. }
  775. // Note that we're using the E as a generic but this is never crafted anyways.
  776. pub struct FromNodeIterator;
  777. impl<T, I> IntoDynNode<FromNodeIterator> for T
  778. where
  779. T: Iterator<Item = I>,
  780. I: IntoVNode,
  781. {
  782. fn into_dyn_node(self) -> DynamicNode {
  783. let children: Vec<_> = self.into_iter().map(|node| node.into_vnode()).collect();
  784. if children.is_empty() {
  785. DynamicNode::default()
  786. } else {
  787. DynamicNode::Fragment(children)
  788. }
  789. }
  790. }
  791. /// A value that can be converted into an attribute value
  792. pub trait IntoAttributeValue {
  793. /// Convert into an attribute value
  794. fn into_value(self) -> AttributeValue;
  795. }
  796. impl IntoAttributeValue for AttributeValue {
  797. fn into_value(self) -> AttributeValue {
  798. self
  799. }
  800. }
  801. impl IntoAttributeValue for &str {
  802. fn into_value(self) -> AttributeValue {
  803. AttributeValue::Text(self.to_string())
  804. }
  805. }
  806. impl IntoAttributeValue for String {
  807. fn into_value(self) -> AttributeValue {
  808. AttributeValue::Text(self)
  809. }
  810. }
  811. impl IntoAttributeValue for f64 {
  812. fn into_value(self) -> AttributeValue {
  813. AttributeValue::Float(self)
  814. }
  815. }
  816. impl IntoAttributeValue for i64 {
  817. fn into_value(self) -> AttributeValue {
  818. AttributeValue::Int(self)
  819. }
  820. }
  821. impl IntoAttributeValue for bool {
  822. fn into_value(self) -> AttributeValue {
  823. AttributeValue::Bool(self)
  824. }
  825. }
  826. impl IntoAttributeValue for Arguments<'_> {
  827. fn into_value(self) -> AttributeValue {
  828. AttributeValue::Text(self.to_string())
  829. }
  830. }
  831. impl IntoAttributeValue for Box<dyn AnyValue> {
  832. fn into_value(self) -> AttributeValue {
  833. AttributeValue::Any(self)
  834. }
  835. }
  836. impl<T: IntoAttributeValue> IntoAttributeValue for Option<T> {
  837. fn into_value(self) -> AttributeValue {
  838. match self {
  839. Some(val) => val.into_value(),
  840. None => AttributeValue::None,
  841. }
  842. }
  843. }
  844. /// A trait for anything that has a dynamic list of attributes
  845. pub trait HasAttributes {
  846. /// Push an attribute onto the list of attributes
  847. fn push_attribute(
  848. self,
  849. name: &'static str,
  850. ns: Option<&'static str>,
  851. attr: impl IntoAttributeValue,
  852. volatile: bool,
  853. ) -> Self;
  854. }