1use std::fmt;
4use std::num::NonZeroU32;
5
6use crate::*;
7
8is_not_ssr!(
9 mod dom_node;
10 #[cfg(feature = "hydrate")]
11 mod hydrate_node;
12);
13is_ssr!(
14 mod ssr_node;
15);
16mod dom_render;
17mod ssr_render;
18
19#[cfg(rust_analyzer)]
21mod dom_node;
22#[cfg(rust_analyzer)]
23mod hydrate_node;
24
25#[cfg_not_ssr]
26pub use dom_node::*;
27pub use dom_render::*;
28#[cfg_not_ssr]
29#[cfg(feature = "hydrate")]
30pub use hydrate_node::*;
31#[cfg_ssr]
32pub use ssr_node::*;
33pub use ssr_render::*;
34
35pub trait ViewHtmlNode: ViewNode {
37 fn create_element(tag: Cow<'static, str>) -> Self;
39 fn create_element_ns(namespace: &'static str, tag: Cow<'static, str>) -> Self;
41 fn create_text_node(text: Cow<'static, str>) -> Self;
43 fn create_dynamic_text_node(text: Cow<'static, str>) -> Self {
45 Self::create_text_node(text)
46 }
47 fn create_marker_node() -> Self;
49
50 fn set_attribute(&mut self, name: Cow<'static, str>, value: StringAttribute);
52 fn set_bool_attribute(&mut self, name: Cow<'static, str>, value: BoolAttribute);
54 fn set_property(&mut self, name: Cow<'static, str>, value: MaybeDyn<JsValue>);
56 fn set_event_handler(
58 &mut self,
59 name: Cow<'static, str>,
60 handler: impl FnMut(web_sys::Event) + 'static,
61 );
62 fn set_inner_html(&mut self, inner_html: Cow<'static, str>);
64
65 fn as_web_sys(&self) -> &web_sys::Node;
67 fn from_web_sys(node: web_sys::Node) -> Self;
69}
70
71pub trait AsHtmlNode {
73 fn as_html_node(&mut self) -> &mut HtmlNode;
74}
75
76thread_local! {
77 pub(crate) static IS_HYDRATING: Cell<bool> = const { Cell::new(false) };
79}
80
81pub fn is_hydrating() -> bool {
83 IS_HYDRATING.with(Cell::get)
84}
85
86#[derive(Debug, Clone, Copy)]
88pub(crate) struct HydrationRegistry {
89 next_key: Signal<HydrationKey>,
90}
91
92#[cfg_attr(not(feature = "hydrate"), allow(dead_code))]
94impl HydrationRegistry {
95 pub fn new() -> Self {
96 HydrationRegistry {
97 next_key: create_signal(HydrationKey {
98 suspense: 0,
99 element: 0,
100 }),
101 }
102 }
103
104 pub fn next_key(self) -> HydrationKey {
106 let key = self.next_key.get_untracked();
107 self.next_key.set_silent(HydrationKey {
108 suspense: key.suspense,
109 element: key.element + 1,
110 });
111 key
112 }
113
114 pub fn in_suspense_scope<T>(suspense: NonZeroU32, f: impl FnOnce() -> T) -> T {
118 let mut ret = None;
119 create_child_scope(|| {
120 provide_context(HydrationRegistry {
121 next_key: create_signal(HydrationKey {
122 suspense: suspense.get(),
123 element: 0,
124 }),
125 });
126 ret = Some(f());
127 });
128 ret.unwrap()
129 }
130}
131
132impl Default for HydrationRegistry {
133 fn default() -> Self {
134 Self::new()
135 }
136}
137
138#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
139pub struct HydrationKey {
140 pub suspense: u32,
142 pub element: u32,
144}
145
146impl fmt::Display for HydrationKey {
147 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148 write!(f, "{}.{}", self.suspense, self.element)
149 }
150}
151
152impl HydrationKey {
153 pub fn parse(s: &str) -> Option<Self> {
154 let mut parts = s.split('.');
155 let suspense = parts.next()?.parse().ok()?;
156 let element = parts.next()?.parse().ok()?;
157 Some(HydrationKey { suspense, element })
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164
165 #[test]
166 fn display_hydration_key() {
167 let key = HydrationKey {
168 suspense: 1,
169 element: 2,
170 };
171 assert_eq!(key.to_string(), "1.2");
172 }
173
174 #[test]
175 fn parse_hydration_key() {
176 assert_eq!(
177 HydrationKey::parse("1.2"),
178 Some(HydrationKey {
179 suspense: 1,
180 element: 2
181 })
182 );
183 assert_eq!(HydrationKey::parse("1"), None);
184 }
185}