#[cfg(feature = "web")]
pub use self::create_element::VIRTUAL_NODE_MARKER_PROPERTY;
#[cfg(feature = "web")]
pub use self::event::EventAttribFn;
pub use self::iterable_nodes::*;
pub use self::velement::*;
pub use self::vtext::*;
use crate::event::{EventHandler, RealDom};
use std::fmt;
pub mod event;
pub mod test_utils;
#[cfg(feature = "web")]
mod create_element;
mod iterable_nodes;
mod velement;
mod vtext;
#[cfg(feature = "web")]
pub type VirtualNodeWebSys = VirtualNode<web_sys::Window>;
pub enum VirtualNode<Dom: RealDom> {
Element(VirtualElement<Dom>),
Text(VirtualText),
}
impl<Dom: RealDom> PartialEq for VirtualNode<Dom> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Element(lhs), Self::Element(rhs)) => lhs == rhs,
(Self::Text(lhs), Self::Text(rhs)) => lhs == rhs,
_ => false,
}
}
}
impl<Dom: RealDom> VirtualNode<Dom> {
pub fn new_element<S>(tag: S) -> Self
where
S: Into<String>,
{
VirtualNode::Element(VirtualElement::new(tag))
}
pub fn new_text<S>(text: S) -> Self
where
S: Into<String>,
{
VirtualNode::Text(VirtualText::new(text.into()))
}
pub fn as_elem(&self) -> Option<&VirtualElement<Dom>> {
match self {
VirtualNode::Element(ref element_node) => Some(element_node),
_ => None,
}
}
pub fn as_elem_mut(&mut self) -> Option<&mut VirtualElement<Dom>> {
match self {
VirtualNode::Element(ref mut element_node) => Some(element_node),
_ => None,
}
}
pub fn as_text(&self) -> Option<&VirtualText> {
match self {
VirtualNode::Text(ref text_node) => Some(text_node),
_ => None,
}
}
pub fn as_text_mut(&mut self) -> Option<&mut VirtualText> {
match self {
VirtualNode::Text(ref mut text_node) => Some(text_node),
_ => None,
}
}
pub fn map_real_dom<New: RealDom>(
self,
convert_event: &dyn Fn(EventHandler<Dom>) -> EventHandler<New>,
) -> VirtualNode<New> {
match self {
VirtualNode::Text(text) => VirtualNode::Text(text),
VirtualNode::Element(elem) => {
let children: Vec<VirtualNode<New>> = elem
.children
.into_iter()
.map(|old| old.map_real_dom::<New>(convert_event))
.collect();
VirtualNode::Element(VirtualElement {
tag: elem.tag,
attrs: elem.attrs,
events: elem.events.convert_all(convert_event),
children,
special_attributes: elem.special_attributes,
})
}
}
}
pub fn insert_space_before_text(&mut self) {
match self {
VirtualNode::Text(text_node) => {
text_node.text = " ".to_string() + &text_node.text;
}
_ => {}
}
}
pub fn insert_space_after_text(&mut self) {
match self {
VirtualNode::Text(text_node) => {
text_node.text += " ";
}
_ => {}
}
}
}
#[cfg(feature = "web")]
impl VirtualNode<web_sys::Window> {
pub fn create_dom_node(
&self,
events: &mut self::event::VirtualEvents<web_sys::Window>,
) -> (web_sys::Node, crate::event::VirtualEventNode) {
match self {
VirtualNode::Text(text_node) => (
text_node.create_text_node().into(),
events.create_text_node(),
),
VirtualNode::Element(element_node) => {
let (elem, events) = element_node.create_element_node(events);
(elem.into(), events)
}
}
}
}
pub trait View<Dom: RealDom> {
fn render(&self) -> VirtualNode<Dom>;
}
impl<V, Dom: RealDom> From<&V> for VirtualNode<Dom>
where
V: View<Dom>,
{
fn from(v: &V) -> Self {
v.render()
}
}
impl<Dom: RealDom> From<VirtualText> for VirtualNode<Dom> {
fn from(other: VirtualText) -> Self {
VirtualNode::Text(other)
}
}
impl<Dom: RealDom> From<VirtualElement<Dom>> for VirtualNode<Dom> {
fn from(other: VirtualElement<Dom>) -> Self {
VirtualNode::Element(other)
}
}
impl<Dom: RealDom> From<&str> for VirtualNode<Dom> {
fn from(other: &str) -> Self {
VirtualNode::new_text(other)
}
}
impl<Dom: RealDom> From<String> for VirtualNode<Dom> {
fn from(other: String) -> Self {
VirtualNode::new_text(other.as_str())
}
}
impl<Dom: RealDom> IntoIterator for VirtualNode<Dom> {
type Item = VirtualNode<Dom>;
type IntoIter = ::std::vec::IntoIter<VirtualNode<Dom>>;
fn into_iter(self) -> Self::IntoIter {
vec![self].into_iter()
}
}
impl<Dom: RealDom> Into<::std::vec::IntoIter<VirtualNode<Dom>>> for VirtualNode<Dom> {
fn into(self) -> ::std::vec::IntoIter<VirtualNode<Dom>> {
self.into_iter()
}
}
impl<Dom: RealDom> fmt::Debug for VirtualNode<Dom> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
VirtualNode::Element(e) => write!(f, "Node::{:?}", e),
VirtualNode::Text(t) => write!(f, "Node::{:?}", t),
}
}
}
impl<Dom: RealDom> fmt::Display for VirtualNode<Dom> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
VirtualNode::Element(element) => write!(f, "{}", element),
VirtualNode::Text(text) => write!(f, "{}", text),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn self_closing_tag_to_string() {
let node = VirtualNode::<()>::new_element("br");
assert_eq!(&node.to_string(), "<br>");
}
#[test]
fn to_string() {
let mut node = VirtualNode::Element(VirtualElement::<()>::new("div"));
node.as_elem_mut()
.unwrap()
.attrs
.insert("id".into(), "some-id".into());
let mut child = VirtualNode::Element(VirtualElement::new("span"));
let text = VirtualNode::Text(VirtualText::new("Hello world"));
child.as_elem_mut().unwrap().children.push(text);
node.as_elem_mut().unwrap().children.push(child);
let expected = r#"<div id="some-id"><span>Hello world</span></div>"#;
assert_eq!(node.to_string(), expected);
}
#[test]
fn boolean_attribute_true_shown() {
let mut button = VirtualElement::<()>::new("button");
button.attrs.insert("disabled".into(), true.into());
let expected = "<button disabled></button>";
let button = VirtualNode::Element(button).to_string();
assert_eq!(button.to_string(), expected);
}
#[test]
fn boolean_attribute_false_ignored() {
let mut button = VirtualElement::<()>::new("button");
button.attrs.insert("disabled".into(), false.into());
let expected = "<button></button>";
let button = VirtualNode::Element(button).to_string();
assert_eq!(button.to_string(), expected);
}
}