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