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
use wasm_bindgen::JsCast;
use web_sys::{Element, HtmlCollection, HtmlElement, HtmlInputElement, Node, NodeList};
use yew::NodeRef;

/// Focus an HTML input element.
///
/// The ref must point to an [`HtmlElement`], if it does not, the function does nothing.
pub fn focus(node_ref: &NodeRef) {
    node_ref.focus();
}

/// Retrieve the value of an [`HtmlInputElement`].
///
/// The ref must point to an [`HtmlInputElement`], if it does not, the function will return [`None`].
pub fn value(node_ref: &NodeRef) -> Option<String> {
    node_ref
        .cast::<HtmlInputElement>()
        .map(|input| input.value())
}

/// Support working with [`web_sys::Element`]
pub trait ElementSupport {
    /// Call [`Element::contains`], or return `false` if this is not an [`Element`].
    fn contains(&self, target: Option<web_sys::EventTarget>) -> bool;
}

impl ElementSupport for NodeRef {
    fn contains(&self, target: Option<web_sys::EventTarget>) -> bool {
        let target = target.as_ref().and_then(|target| target.dyn_ref::<Node>());
        if let Some(element) = self.cast::<Element>() {
            element.contains(target)
        } else {
            false
        }
    }
}

/// Support working with [`web_sys::HtmlElement`]
pub trait HtmlElementSupport {
    /// Call [`HtmlElement::focus`] if this is an [`HtmlElement`].
    fn focus(&self);
}

impl HtmlElementSupport for NodeRef {
    fn focus(&self) {
        if let Some(input) = self.cast::<HtmlElement>() {
            let _ = input.focus();
        }
    }
}

/// Allow iterating over a [`NodeList`].
pub struct IterableNodeList<'a>(pub &'a NodeList);

impl<'a> IntoIterator for IterableNodeList<'a> {
    type Item = Node;
    type IntoIter = NodeListIter<'a>;

    fn into_iter(self) -> Self::IntoIter {
        Self::IntoIter::new(self.0)
    }
}

#[doc(hidden)]
pub struct NodeListIter<'a> {
    list: &'a NodeList,
    index: u32,
}

impl<'a> NodeListIter<'a> {
    pub fn new(list: &'a NodeList) -> Self {
        Self { list, index: 0 }
    }
}

impl<'a> Iterator for NodeListIter<'a> {
    type Item = Node;

    fn next(&mut self) -> Option<Self::Item> {
        let next = self.list.item(self.index);
        self.index += 1;
        next
    }
}

/// Allow iterating over an [`HtmlCollection`].
pub struct IterableHtmlCollection<'a>(pub &'a HtmlCollection);

impl<'a> IntoIterator for IterableHtmlCollection<'a> {
    type Item = Element;
    type IntoIter = HtmlCollectionIter<'a>;

    fn into_iter(self) -> Self::IntoIter {
        Self::IntoIter::new(self.0)
    }
}

#[doc(hidden)]
pub struct HtmlCollectionIter<'a> {
    list: &'a HtmlCollection,
    index: u32,
}

impl<'a> HtmlCollectionIter<'a> {
    pub fn new(list: &'a HtmlCollection) -> Self {
        Self { list, index: 0 }
    }
}

impl<'a> Iterator for HtmlCollectionIter<'a> {
    type Item = Element;

    fn next(&mut self) -> Option<Self::Item> {
        let next = self.list.item(self.index);
        self.index += 1;
        next
    }
}