1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
//! DOM and virtual DOM types.
use std::fmt::Display;
use std::marker::PhantomData;
use super::OutputType;
use elements::{FlowContent, PhrasingContent};
use events::Events;
use htmlescape::encode_minimal;
/// A boxed DOM tree, as returned from the `html!` macro.
///
/// # Examples
///
/// ```
/// # use typed_html::html;
/// # use typed_html::dom::DOMTree;
/// # fn main() {
/// let tree: DOMTree<String> = html!(
/// <div class="hello">
/// <p>"Hello Joe!"</p>
/// </div>
/// );
/// let rendered_tree: String = tree.to_string();
/// # }
/// ```
pub type DOMTree<T> = Box<Node<T>>;
/// An untyped representation of an HTML node.
///
/// This structure is designed to be easily walked in order to render a DOM tree
/// or diff against an existing tree. It's the stringly typed version of
/// [`Node`][Node].
///
/// It can be constructed from any ['Node'][Node]:
///
/// ```no_compile
/// html!(
/// <p>"But how does she "<em>"eat?"</em></p>
/// ).vnode()
/// ```
///
/// [Node]: trait.Node.html
pub enum VNode<'a, T: OutputType + 'a> {
Text(&'a str),
Element(VElement<'a, T>),
}
/// An untyped representation of an HTML element.
pub struct VElement<'a, T: OutputType + 'a> {
pub name: &'static str,
pub attributes: Vec<(&'static str, String)>,
pub events: &'a mut Events<T>,
pub children: Vec<VNode<'a, T>>,
}
/// Trait for rendering a typed HTML node.
///
/// All [HTML elements][elements] implement this, in addition to
/// [`TextNode`][TextNode].
///
/// It implements [`Display`][Display] for rendering to strings, and the
/// [`vnode()`][vnode] method can be used to render a virtual DOM structure.
///
/// [Display]: https://doc.rust-lang.org/std/fmt/trait.Display.html
/// [TextNode]: struct.TextNode.html
/// [elements]: ../elements/index.html
/// [vnode]: #tymethod.vnode
pub trait Node<T: OutputType>: Display {
/// Render the node into a [`VNode`][VNode] tree.
///
/// [VNode]: enum.VNode.html
fn vnode(&mut self) -> VNode<T>;
}
impl<T> IntoIterator for Box<dyn Node<T>> where T: OutputType {
type Item = Box<dyn Node<T>>;
type IntoIter = std::vec::IntoIter<Box<dyn Node<T>>>;
fn into_iter(self) -> Self::IntoIter {
vec![self].into_iter()
}
}
/// Trait for querying a typed HTML element.
///
/// All [HTML elements][elements] implement this.
///
/// [elements]: ../elements/index.html
pub trait Element<T: OutputType>: Node<T> {
/// Get the name of the element.
fn name() -> &'static str;
/// Get a list of the attribute names for this element.
///
/// This includes only the typed attributes, not any `data-` attributes
/// defined on this particular element instance.
///
/// This is probably not useful unless you're the `html!` macro.
fn attribute_names() -> &'static [&'static str];
/// Get a list of the element names of required children for this element.
///
/// This is probably not useful unless you're the `html!` macro.
fn required_children() -> &'static [&'static str];
/// Get a list of the defined attribute pairs for this element.
///
/// This will convert attribute values into strings and return a vector of
/// key/value pairs.
fn attributes(&self) -> Vec<(&'static str, String)>;
}
/// An HTML text node.
pub struct TextNode<T: OutputType>(String, PhantomData<T>);
/// Macro for creating text nodes.
///
/// Returns a boxed text node of type `Box<TextNode>`.
///
/// These can be created inside the `html!` macro directly by using string
/// literals. This macro is useful for creating text macros inside code blocks.
///
/// # Examples
///
/// ```no_compile
/// html!(
/// <p>{ text!("Hello Joe!") }</p>
/// )
/// ```
///
/// ```no_compile
/// html!(
/// <p>{ text!("Hello {}!", "Robert") }</p>
/// )
/// ```
#[macro_export]
macro_rules! text {
($t:expr) => {
Box::new($crate::dom::TextNode::new($t))
};
($format:tt, $($tail:tt),*) => {
Box::new($crate::dom::TextNode::new(format!($format, $($tail),*)))
};
}
impl<T: OutputType> TextNode<T> {
/// Construct a text node.
///
/// The preferred way to construct a text node is with the [`text!()`][text]
/// macro.
///
/// [text]: ../macro.text.html
pub fn new<S: Into<String>>(s: S) -> Self {
TextNode(s.into(), PhantomData)
}
}
impl<T: OutputType> Display for TextNode<T> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
f.write_str(&encode_minimal(&self.0))
}
}
impl<T: OutputType> Node<T> for TextNode<T> {
fn vnode(&'_ mut self) -> VNode<'_, T> {
VNode::Text(&self.0)
}
}
impl<T: OutputType> IntoIterator for TextNode<T> {
type Item = TextNode<T>;
type IntoIter = std::vec::IntoIter<TextNode<T>>;
fn into_iter(self) -> Self::IntoIter {
vec![self].into_iter()
}
}
impl<T: OutputType> IntoIterator for Box<TextNode<T>> {
type Item = Box<TextNode<T>>;
type IntoIter = std::vec::IntoIter<Box<TextNode<T>>>;
fn into_iter(self) -> Self::IntoIter {
vec![self].into_iter()
}
}
impl<T: OutputType> FlowContent<T> for TextNode<T> {}
impl<T: OutputType> PhrasingContent<T> for TextNode<T> {}