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
use wasm_bindgen::JsValue;

use crate::IntoOptionalElement;

mod inner {
    #[derive(Debug, Clone)]
    pub enum Element {
        /// The element created from js and is same to clone.
        JsElement(react_sys::Element),
        /// bridge a js component so that
        /// it can be created in rust
        JsBridge(crate::JsBridgeElement),
        /// Bridge a rust use_render fn
        UseRender(crate::UseRenderElement),
        /// This could be represented as JsBridge but
        /// extracted out to improve performance
        Fragment(crate::FragmentElement),
    }
}

use inner::Element as ElementInner;

///
/// ### Why not [`react_sys::Element`]
///
/// `react_sys::Element`, created from ReactJs,
///  can be cloned freely.
///
/// However, when it is created from frender component,
/// it contains data from rust,
/// which can't be stored in react_sys::Element.
///
/// Due to the same reason, [`Element`]
/// implements [`From<react_sys::Element>`]
/// but not [`Into<react_sys::Element>`]
#[derive(Clone)]
pub struct Element {
    inner: ElementInner,
}

impl std::fmt::Debug for Element {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.inner.fmt(f)
    }
}

impl Element {
    #[inline]
    pub fn from_js_element(el: react_sys::Element) -> Self {
        Self {
            inner: ElementInner::JsElement(el),
        }
    }

    #[inline]
    pub(crate) fn unsafe_into_js_element(self) -> react_sys::Element {
        match self.inner {
            ElementInner::JsElement(js) => js,
            ElementInner::JsBridge(br) => br.unsafe_create_element_js(),
            ElementInner::UseRender(re) => re.unsafe_create_element_js(),
            ElementInner::Fragment(fe) => fe.unsafe_create_element_js(),
        }
    }

    #[inline]
    pub fn bridge_use_render<E: IntoOptionalElement, F: 'static + Fn() -> E>(
        use_render: F,
        key: Option<crate::Key>,
        debug_component_name: Option<JsValue>,
        debug_props: Option<JsValue>,
    ) -> Self {
        let bridge = crate::UseRenderElement::wrap_use_render(
            use_render,
            key,
            debug_component_name,
            debug_props,
        );

        Self::bridge_use_render_element(bridge)
    }

    #[inline]
    pub fn bridge_use_render_element(el: crate::UseRenderElement) -> Self {
        Self {
            inner: ElementInner::UseRender(el),
        }
    }

    #[inline]
    pub fn bridge_js(js: crate::JsBridgeElement) -> Self {
        Self {
            inner: ElementInner::JsBridge(js),
        }
    }

    #[inline]
    pub fn fragment(fe: crate::FragmentElement) -> Self {
        Self {
            inner: ElementInner::Fragment(fe),
        }
    }

    #[doc(hidden)]
    #[inline]
    pub fn private_from_element(el: Self) -> Self {
        el
    }
}

impl crate::Node for Element {
    #[inline]
    fn to_node(&self) -> crate::AnyNode {
        self.clone().into_node()
    }

    #[inline]
    fn to_children(&self) -> Option<crate::Children> {
        self.clone().into_children()
    }

    #[inline]
    fn into_node(self) -> crate::AnyNode {
        crate::AnyNode::Element(self)
    }

    #[inline]
    fn into_children(self) -> Option<crate::Children> {
        Some(crate::Children::from_single(self.into_node()))
    }
}

impl From<react_sys::Element> for Element {
    #[inline]
    fn from(el: react_sys::Element) -> Self {
        Self::from_js_element(el)
    }
}