leptos_node_ref/
any_node_ref.rs1use std::marker::PhantomData;
2
3use leptos::{
4 attr::{Attribute, NextAttribute},
5 html::ElementType,
6 prelude::{
7 DefinedAt, Get, NodeRef, ReadUntracked, RwSignal, Set, Track,
8 guards::{Derefable, ReadGuard},
9 },
10 tachys::{html::node_ref::NodeRefContainer, renderer::types::Element},
11};
12use send_wrapper::SendWrapper;
13
14#[derive(Debug)]
16pub struct AnyNodeRef(RwSignal<Option<SendWrapper<Element>>>);
17
18impl AnyNodeRef {
19 #[track_caller]
21 pub fn new() -> Self {
22 Self(RwSignal::new(None))
23 }
24}
25
26impl Default for AnyNodeRef {
27 fn default() -> Self {
28 Self::new()
29 }
30}
31
32impl Clone for AnyNodeRef {
33 fn clone(&self) -> Self {
34 *self
35 }
36}
37
38impl Copy for AnyNodeRef {}
39
40impl DefinedAt for AnyNodeRef {
41 fn defined_at(&self) -> Option<&'static std::panic::Location<'static>> {
42 self.0.defined_at()
43 }
44}
45
46impl<T: ElementType> From<NodeRef<T>> for AnyNodeRef
47where
48 NodeRef<T>: IntoAnyNodeRef,
49{
50 fn from(value: NodeRef<T>) -> Self {
51 value.into_any()
52 }
53}
54
55impl<E: ElementType> NodeRefContainer<E> for AnyNodeRef {
56 fn load(self, el: &Element) {
57 self.0.set(Some(SendWrapper::new(el.clone())));
58 }
59}
60
61impl ReadUntracked for AnyNodeRef {
62 type Value = ReadGuard<Option<Element>, Derefable<Option<Element>>>;
63
64 fn try_read_untracked(&self) -> Option<Self::Value> {
65 Some(ReadGuard::new(Derefable(
66 self.0.try_read_untracked()?.as_deref().cloned(),
67 )))
68 }
69}
70
71impl Track for AnyNodeRef {
72 fn track(&self) {
73 self.0.track();
74 }
75}
76
77pub trait IntoAnyNodeRef {
79 fn into_any(self) -> AnyNodeRef;
81}
82
83impl<E> IntoAnyNodeRef for NodeRef<E>
84where
85 E: ElementType,
86 E::Output: AsRef<Element>,
87 NodeRef<E>: Get<Value = Option<E::Output>>,
88{
89 fn into_any(self) -> AnyNodeRef {
90 let any_ref = AnyNodeRef::new();
91 if let Some(element) = self.get() {
92 NodeRefContainer::<E>::load(any_ref, element.as_ref());
93 }
94 any_ref
95 }
96}
97
98impl IntoAnyNodeRef for AnyNodeRef {
99 fn into_any(self) -> AnyNodeRef {
100 self
101 }
102}
103
104#[derive(Debug)]
108pub struct AnyNodeRefAttr<E, C> {
109 container: C,
110 ty: PhantomData<E>,
111}
112
113impl<E, C> Clone for AnyNodeRefAttr<E, C>
114where
115 C: Clone,
116{
117 fn clone(&self) -> Self {
118 Self {
119 container: self.container.clone(),
120 ty: PhantomData,
121 }
122 }
123}
124
125impl<E, C> Attribute for AnyNodeRefAttr<E, C>
126where
127 E: ElementType + 'static,
128 C: NodeRefContainer<E> + Clone + 'static,
129 Element: PartialEq,
130{
131 const MIN_LENGTH: usize = 0;
132 type State = Element;
133 type AsyncOutput = Self;
134 type Cloneable = Self;
135 type CloneableOwned = Self;
136
137 #[inline(always)]
138 fn html_len(&self) -> usize {
139 0
140 }
141
142 fn to_html(
143 self,
144 _buf: &mut String,
145 _class: &mut String,
146 _style: &mut String,
147 _inner_html: &mut String,
148 ) {
149 }
150
151 fn hydrate<const FROM_SERVER: bool>(self, el: &Element) -> Self::State {
152 self.container.load(el);
153 el.clone()
154 }
155
156 fn build(self, el: &Element) -> Self::State {
157 self.container.load(el);
158 el.clone()
159 }
160
161 fn rebuild(self, state: &mut Self::State) {
162 self.container.load(state);
163 }
164
165 fn into_cloneable(self) -> Self::Cloneable {
166 self
167 }
168
169 fn into_cloneable_owned(self) -> Self::CloneableOwned {
170 self
171 }
172
173 fn dry_resolve(&mut self) {}
174
175 async fn resolve(self) -> Self::AsyncOutput {
176 self
177 }
178}
179
180impl<E, C> NextAttribute for AnyNodeRefAttr<E, C>
181where
182 E: ElementType + 'static,
183 C: NodeRefContainer<E> + Clone + 'static,
184 Element: PartialEq,
185{
186 type Output<NewAttr: Attribute> = (Self, NewAttr);
187
188 fn add_any_attr<NewAttr: Attribute>(self, new_attr: NewAttr) -> Self::Output<NewAttr> {
189 (self, new_attr)
190 }
191}
192
193pub fn any_node_ref<E, C>(container: C) -> AnyNodeRefAttr<E, C>
197where
198 E: ElementType,
199 C: NodeRefContainer<E>,
200{
201 AnyNodeRefAttr {
202 container,
203 ty: PhantomData,
204 }
205}
206
207pub mod prelude {
208 pub use super::*;
209 pub use AnyNodeRef;
210 pub use IntoAnyNodeRef;
211 pub use any_node_ref;
212}
213
214#[cfg(test)]
215mod tests {
216 use leptos::{html, prelude::*};
217
218 use super::{any_node_ref, prelude::*};
219
220 #[test]
221 fn test_any_node_ref_creation() {
222 let node_ref = AnyNodeRef::new();
223 assert!(node_ref.get().is_none(), "New AnyNodeRef should be empty");
224 }
225
226 #[test]
227 fn test_to_any_node_ref() {
228 let div_ref: NodeRef<html::Div> = NodeRef::new();
229 let any_ref = div_ref.into_any();
230 assert!(
231 any_ref.get().is_none(),
232 "Converted AnyNodeRef should be initially empty"
233 );
234 }
235
236 #[test]
237 fn test_clone_and_copy() {
238 let node_ref = AnyNodeRef::new();
239 let cloned_ref = node_ref;
240 let _copied_ref = cloned_ref; assert!(
242 cloned_ref.get().is_none(),
243 "Cloned AnyNodeRef should be empty"
244 );
245 }
246
247 #[test]
248 fn test_default() {
249 let node_ref = AnyNodeRef::default();
250 assert!(
251 node_ref.get().is_none(),
252 "Default AnyNodeRef should be empty"
253 );
254 }
255
256 #[test]
257 fn test_into_any_node_ref_trait() {
258 let div_ref: NodeRef<html::Div> = NodeRef::new();
259 let _any_ref: AnyNodeRef = div_ref.into_any();
260
261 let input_ref: NodeRef<html::Input> = NodeRef::new();
262 let _any_input_ref: AnyNodeRef = input_ref.into_any();
263 }
264
265 #[test]
266 fn test_from_node_ref() {
267 let div_ref: NodeRef<html::Div> = NodeRef::new();
268 let _any_ref: AnyNodeRef = div_ref.into();
269 }
270
271 #[test]
272 fn test_any_node_ref_attr() {
273 let node_ref = AnyNodeRef::new();
274 let _attr = any_node_ref::<html::Div, _>(node_ref);
275 }
276
277 #[test]
278 fn test_defined_at() {
279 let node_ref = AnyNodeRef::new();
280 assert!(node_ref.defined_at().is_some());
281 }
282
283 #[test]
284 fn test_track_and_untracked() {
285 let node_ref = AnyNodeRef::new();
286 node_ref.track();
288 let _untracked = node_ref.try_read_untracked();
289 }
290
291 #[test]
292 fn test_into_any_identity() {
293 let node_ref = AnyNodeRef::new();
294 let same_ref = node_ref.into_any();
295
296 assert!(node_ref.get().is_none());
299 assert!(same_ref.get().is_none());
300
301 assert_eq!(node_ref.defined_at(), same_ref.defined_at());
306 }
307}