1//! Implementation of rendering backend.
23use std::fmt;
4use std::num::NonZeroU32;
56use crate::*;
78cfg_not_ssr_item!(
9mod dom_node;
10);
11cfg_not_ssr_item!(
12#[cfg(feature = "hydrate")]
13mod hydrate_node;
14);
15cfg_ssr_item!(
16mod ssr_node;
17);
18mod dom_render;
19mod ssr_render;
2021// We add this so that we get IDE support in Rust Analyzer.
22#[cfg(rust_analyzer)]
23mod dom_node;
24#[cfg(rust_analyzer)]
25mod hydrate_node;
2627#[cfg_not_ssr]
28pub use dom_node::*;
29pub use dom_render::*;
30#[cfg_not_ssr]
31#[cfg(feature = "hydrate")]
32pub use hydrate_node::*;
33#[cfg_ssr]
34pub use ssr_node::*;
35pub use ssr_render::*;
3637/// A trait that should be implemented for anything that represents an HTML node.
38pub trait ViewHtmlNode: ViewNode {
39/// Create a new HTML element.
40fn create_element(tag: Cow<'static, str>) -> Self;
41/// Create a new HTML element with a XML namespace.
42fn create_element_ns(namespace: &'static str, tag: Cow<'static, str>) -> Self;
43/// Create a new HTML text node.
44fn create_text_node(text: Cow<'static, str>) -> Self;
45/// Create a new HTML text node whose value will be changed dynamically.
46fn create_dynamic_text_node(text: Cow<'static, str>) -> Self {
47Self::create_text_node(text)
48 }
49/// Create a new HTML marker (comment) node.
50fn create_marker_node() -> Self;
5152/// Set an HTML attribute.
53fn set_attribute(&mut self, name: Cow<'static, str>, value: StringAttribute);
54/// Set a boolean HTML attribute.
55fn set_bool_attribute(&mut self, name: Cow<'static, str>, value: BoolAttribute);
56/// Set a JS property on an element.
57fn set_property(&mut self, name: Cow<'static, str>, value: MaybeDyn<JsValue>);
58/// Set an event handler on an element.
59fn set_event_handler(
60&mut self,
61 name: Cow<'static, str>,
62 handler: impl FnMut(web_sys::Event) + 'static,
63 );
64/// Set the inner HTML value of an element.
65fn set_inner_html(&mut self, inner_html: Cow<'static, str>);
6667/// Return the raw web-sys node.
68fn as_web_sys(&self) -> &web_sys::Node;
69/// Wrap a raw web-sys node.
70fn from_web_sys(node: web_sys::Node) -> Self;
71}
7273/// A trait for unwrapping a type into an `HtmlNode`.
74pub trait AsHtmlNode {
75fn as_html_node(&mut self) -> &mut HtmlNode;
76}
7778thread_local! {
79/// Whether we are in hydration mode or not.
80pub(crate) static IS_HYDRATING: Cell<bool> = const { Cell::new(false) };
81}
8283/// Returns whether we are currently hydrating or not.
84pub fn is_hydrating() -> bool {
85 IS_HYDRATING.with(Cell::get)
86}
8788/// A struct for keeping track of state used for hydration.
89#[derive(Debug, Clone, Copy)]
90pub(crate) struct HydrationRegistry {
91 next_key: Signal<HydrationKey>,
92}
9394// This is only used when hydrating.
95#[cfg_attr(not(feature = "hydrate"), allow(dead_code))]
96impl HydrationRegistry {
97pub fn new() -> Self {
98 HydrationRegistry {
99 next_key: create_signal(HydrationKey {
100 suspense: 0,
101 element: 0,
102 }),
103 }
104 }
105106/// Get the next hydration key and increment the internal state. This new key will be unique.
107pub fn next_key(self) -> HydrationKey {
108let key = self.next_key.get_untracked();
109self.next_key.set_silent(HydrationKey {
110 suspense: key.suspense,
111 element: key.element + 1,
112 });
113 key
114 }
115116/// Run the given function within a suspense scope.
117 ///
118 /// This sets the suspense key to the passed value and resets the element key to 0.
119pub fn in_suspense_scope<T>(suspense: NonZeroU32, f: impl FnOnce() -> T) -> T {
120let mut ret = None;
121 create_child_scope(|| {
122 provide_context(HydrationRegistry {
123 next_key: create_signal(HydrationKey {
124 suspense: suspense.get(),
125 element: 0,
126 }),
127 });
128 ret = Some(f());
129 });
130 ret.unwrap()
131 }
132}
133134impl Default for HydrationRegistry {
135fn default() -> Self {
136Self::new()
137 }
138}
139140#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
141pub struct HydrationKey {
142/// Suspense key, or 0 if not in a suspense boundary.
143pub suspense: u32,
144/// Element key.
145pub element: u32,
146}
147148impl fmt::Display for HydrationKey {
149fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150write!(f, "{}.{}", self.suspense, self.element)
151 }
152}
153154impl HydrationKey {
155pub fn parse(s: &str) -> Option<Self> {
156let mut parts = s.split('.');
157let suspense = parts.next()?.parse().ok()?;
158let element = parts.next()?.parse().ok()?;
159Some(HydrationKey { suspense, element })
160 }
161}
162163#[cfg(test)]
164mod tests {
165use super::*;
166167#[test]
168fn display_hydration_key() {
169let key = HydrationKey {
170 suspense: 1,
171 element: 2,
172 };
173assert_eq!(key.to_string(), "1.2");
174 }
175176#[test]
177fn parse_hydration_key() {
178assert_eq!(
179 HydrationKey::parse("1.2"),
180Some(HydrationKey {
181 suspense: 1,
182 element: 2
183})
184 );
185assert_eq!(HydrationKey::parse("1"), None);
186 }
187}