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
//! This module contains fragments implementation.
use super::{VDiff, VNode, VText};
use html::{Component, Scope};
use std::iter::FromIterator;
use web_sys::Node;

/// This struct represents a fragment of the Virtual DOM tree.
pub struct VList<COMP: Component> {
    /// The list of children nodes. Which also could have own children.
    pub childs: Vec<VNode<COMP>>,
}

impl<COMP: Component> From<Vec<VNode<COMP>>> for VList<COMP> {
    fn from(other: Vec<VNode<COMP>>) -> Self {
        VList { childs: other }
    }
}

impl<COMP: Component> FromIterator<VNode<COMP>> for VList<COMP> {
    fn from_iter<I: IntoIterator<Item = VNode<COMP>>>(iter: I) -> Self {
        let mut list = VList::new();

        for c in iter {
            list.add_child(c);
        }

        list
    }
}

impl<COMP: Component> VList<COMP> {
    /// Creates a new `VTag` instance with `tag` name (cannot be changed later in DOM).
    pub fn new() -> Self {
        VList { childs: Vec::new() }
    }

    /// Add `VNode` child.
    pub fn add_child(&mut self, child: VNode<COMP>) {
        self.childs.push(child);
    }
}

impl<COMP: Component> VDiff for VList<COMP> {
    type Component = COMP;

    fn detach(&mut self, parent: &Node) -> Option<Node> {
        let mut last_sibling = None;
        for mut child in self.childs.drain(..) {
            last_sibling = child.detach(parent);
        }
        last_sibling
    }

    fn apply(
        &mut self,
        parent: &Node,
        precursor: Option<&Node>,
        ancestor: Option<VNode<Self::Component>>,
        env: &Scope<Self::Component>,
    ) -> Option<Node> {
        // Reuse precursor, because fragment reuse parent
        let mut precursor = precursor.map(|node| node.to_owned());
        let mut rights = {
            match ancestor {
                // If element matched this type
                Some(VNode::VList(mut vlist)) => {
                    // Previously rendered items
                    vlist.childs.drain(..).map(Some).collect::<Vec<_>>()
                }
                Some(mut vnode) => {
                    // Use the current node as a single fragment list
                    // and let the `apply` of `VNode` to handle it.
                    vec![Some(vnode)]
                }
                None => Vec::new(),
            }
        };
        // Collect elements of an ancestor if exists or use an empty vec
        // TODO DRY?!
        if self.childs.is_empty() {
            // Fixes: https://github.com/DenisKolodin/yew/issues/294
            // Without a placeholder the next element becomes first
            // and corrupts the order of rendering
            // We use empty text element to stake out a place
            let placeholder = VText::new("".into());
            self.childs.push(placeholder.into());
        }
        let mut lefts = self.childs.iter_mut().map(Some).collect::<Vec<_>>();
        // Process children
        let diff = lefts.len() as i32 - rights.len() as i32;
        if diff > 0 {
            for _ in 0..diff {
                rights.push(None);
            }
        } else if diff < 0 {
            for _ in 0..-diff {
                lefts.push(None);
            }
        }
        for pair in lefts.into_iter().zip(rights) {
            match pair {
                (Some(left), right) => {
                    precursor = left.apply(parent, precursor.as_ref(), right, &env);
                }
                (None, Some(mut right)) => {
                    right.detach(parent);
                }
                (None, None) => {
                    panic!("redundant iterations during diff");
                }
            }
        }
        precursor
    }
}

impl<COMP: Component> std::fmt::Debug for VList<COMP> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "VList [ {:?} ]", self.childs)
    }
}