euv_core/vdom/cast/impl.rs
1use crate::*;
2
3/// Converts a `VirtualNode` into itself via `IntoNode`.
4impl IntoNode for VirtualNode {
5 /// Returns this virtual node as-is.
6 ///
7 /// # Returns
8 ///
9 /// - `VirtualNode` - This same virtual node.
10 fn into_node(self) -> VirtualNode {
11 self
12 }
13}
14
15/// Wraps a `FnMut() -> VirtualNode` closure into a `DynamicNode` via `IntoNode`.
16///
17/// This enables writing `{move || html! { ... }}` directly in HTML markup
18/// without explicit `DynamicNode` construction.
19impl<F> IntoNode for F
20where
21 F: FnMut() -> VirtualNode + 'static,
22{
23 /// Wraps this closure into a `VirtualNode::Dynamic` with a fresh hook context.
24 ///
25 /// # Returns
26 ///
27 /// - `VirtualNode` - A dynamic virtual node wrapping this closure.
28 fn into_node(self) -> VirtualNode {
29 let render_inner: Rc<RefCell<RenderFnInner>> =
30 Rc::new(RefCell::new(RenderFnInner::new(Box::new(self))));
31 VirtualNode::Dynamic(DynamicNode::new(
32 render_inner,
33 crate::reactive::create_hook_context(),
34 ))
35 }
36}
37
38/// Converts a `String` into a text virtual node via `IntoNode`.
39impl IntoNode for String {
40 /// Converts this string into a text virtual node.
41 ///
42 /// # Returns
43 ///
44 /// - `VirtualNode` - A text virtual node.
45 fn into_node(self) -> VirtualNode {
46 VirtualNode::Text(TextNode::new(self, None))
47 }
48}
49
50/// Converts a `&str` into a text virtual node via `IntoNode`.
51impl IntoNode for &str {
52 /// Converts this string slice into a text virtual node.
53 ///
54 /// # Returns
55 ///
56 /// - `VirtualNode` - A text virtual node.
57 fn into_node(self) -> VirtualNode {
58 VirtualNode::Text(TextNode::new(self.to_string(), None))
59 }
60}
61
62/// Converts an `i32` into a text virtual node via `IntoNode`.
63impl IntoNode for i32 {
64 /// Converts this integer into a text virtual node.
65 ///
66 /// # Returns
67 ///
68 /// - `VirtualNode` - A text virtual node.
69 fn into_node(self) -> VirtualNode {
70 VirtualNode::Text(TextNode::new(self.to_string(), None))
71 }
72}
73
74/// Converts a `usize` into a text virtual node via `IntoNode`.
75impl IntoNode for usize {
76 /// Converts this unsigned integer into a text virtual node.
77 ///
78 /// # Returns
79 ///
80 /// - `VirtualNode` - A text virtual node.
81 fn into_node(self) -> VirtualNode {
82 VirtualNode::Text(TextNode::new(self.to_string(), None))
83 }
84}
85
86/// Converts a `bool` into a text virtual node via `IntoNode`.
87impl IntoNode for bool {
88 /// Converts this boolean into a text virtual node.
89 ///
90 /// # Returns
91 ///
92 /// - `VirtualNode` - A text virtual node.
93 fn into_node(self) -> VirtualNode {
94 VirtualNode::Text(TextNode::new(self.to_string(), None))
95 }
96}
97
98/// Converts a signal into a reactive text virtual node via `IntoNode`.
99impl<T> IntoNode for Signal<T>
100where
101 T: Clone + PartialEq + std::fmt::Display + 'static,
102{
103 /// Converts this signal into a reactive text virtual node.
104 ///
105 /// # Returns
106 ///
107 /// - `VirtualNode` - A reactive text virtual node.
108 fn into_node(self) -> VirtualNode {
109 self.as_reactive_text()
110 }
111}
112
113/// Converts a signal into a reactive text node with listener wiring.
114impl<T> AsReactiveText for Signal<T>
115where
116 T: Clone + PartialEq + std::fmt::Display + 'static,
117{
118 /// Creates a reactive text node that auto-updates when the signal changes.
119 ///
120 /// Internally creates a bridge `Signal<String>` that subscribes to the
121 /// source signal and updates the text content on every change.
122 ///
123 /// # Returns
124 ///
125 /// - `VirtualNode` - A text virtual node with reactive signal binding.
126 fn as_reactive_text(&self) -> VirtualNode {
127 let initial: String = self.get().to_string();
128 let string_signal: Signal<String> = Signal::create(initial.clone());
129 let source_signal: Signal<T> = *self;
130 let string_signal_clone: Signal<String> = string_signal;
131 source_signal.replace_subscribe({
132 let source_inner: Signal<T> = source_signal;
133 move || {
134 let new_value: String = source_inner.get().to_string();
135 string_signal_clone.set_silent(new_value);
136 }
137 });
138 VirtualNode::Text(TextNode::new(initial, Some(string_signal)))
139 }
140}
141
142/// Constructs an `EventAdapter` that wraps any event-compatible value.
143impl<T> EventAdapter<T> {
144 /// Returns the inner wrapped value, consuming the adapter.
145 ///
146 /// # Returns
147 ///
148 /// - `T` - The inner value.
149 pub(crate) fn into_inner(self) -> T {
150 self.inner
151 }
152}
153
154/// Adapts a `FnMut(Event)` closure into an `AttributeValue::Event`.
155///
156/// Wraps the closure into a `NativeEventHandler` and returns it as an
157/// event attribute value. This replaces the `__EventWrapper<F>` type
158/// that was previously generated inline by the `html!` macro.
159impl<F> EventAdapter<F>
160where
161 F: FnMut(Event) + 'static,
162{
163 /// Converts the wrapped closure into an event `AttributeValue`.
164 ///
165 /// # Arguments
166 ///
167 /// - `NativeEventName` - The event name enum variant to associate with the handler.
168 ///
169 /// # Returns
170 ///
171 /// - `AttributeValue` - An `AttributeValue::Event` wrapping the handler.
172 pub fn into_attribute(self, event_name: NativeEventName) -> AttributeValue {
173 AttributeValue::Event(NativeEventHandler::create(event_name, self.into_inner()))
174 }
175}
176
177/// Adapts an owned `NativeEventHandler` into an `AttributeValue::Event` directly.
178///
179/// When the user already provides a `NativeEventHandler`, the handler is
180/// re-wrapped with the given `event_name` to ensure the DOM event listener
181/// is bound to the correct event type (e.g., "click" rather than "onclick").
182/// This replaces the `impl __EventWrapper<NativeEventHandler>` that was
183/// previously generated inline.
184impl EventAdapter<NativeEventHandler> {
185 /// Converts the wrapped handler into an event `AttributeValue`.
186 ///
187 /// Re-wraps the handler with the provided `event_name` so that the
188 /// DOM event listener uses the correct event type string.
189 ///
190 /// # Arguments
191 ///
192 /// - `NativeEventName` - The event name to bind the handler to.
193 ///
194 /// # Returns
195 ///
196 /// - `AttributeValue` - An `AttributeValue::Event` containing the re-wrapped handler.
197 pub fn into_attribute(self, event_name: NativeEventName) -> AttributeValue {
198 let mut handler: NativeEventHandler = self.into_inner();
199 handler.set_event_name(event_name.to_string());
200 AttributeValue::Event(handler)
201 }
202}
203
204/// Adapts an `Option<NativeEventHandler>` into an `AttributeValue`.
205///
206/// `Some(handler)` becomes `AttributeValue::Event(handler)` re-wrapped with the
207/// given event name, and `None` becomes `AttributeValue::Text(String::new())`.
208/// This replaces the `impl __EventWrapper<Option<NativeEventHandler>>` that was
209/// previously generated inline by the `html!` macro.
210impl EventAdapter<Option<NativeEventHandler>> {
211 /// Converts the wrapped optional handler into an attribute value.
212 ///
213 /// Re-wraps a `Some` handler with the provided `event_name` so that the
214 /// DOM event listener uses the correct event type string.
215 ///
216 /// # Arguments
217 ///
218 /// - `NativeEventName` - The event name to bind the handler to.
219 ///
220 /// # Returns
221 ///
222 /// - `AttributeValue` - An event attribute if `Some`, otherwise an empty text attribute.
223 pub fn into_attribute(self, event_name: NativeEventName) -> AttributeValue {
224 match self.into_inner() {
225 Some(handler) => EventAdapter::new(handler).into_attribute(event_name),
226 None => AttributeValue::Text(String::new()),
227 }
228 }
229}
230
231/// Constructs an `AttrValueAdapter` that wraps any attribute-compatible value.
232impl<T> AttrValueAdapter<T> {
233 /// Returns the inner wrapped value, consuming the adapter.
234 ///
235 /// # Returns
236 ///
237 /// - `T` - The inner value.
238 pub(crate) fn into_inner(self) -> T {
239 self.inner
240 }
241}
242
243/// Adapts a `FnMut(Event)` closure into a callback `AttributeValue`.
244///
245/// This handles the case where a closure is used as a component callback prop.
246/// The closure is converted via `IntoCallbackAttribute::into_callback_attribute()`.
247/// This replaces the `__IsClosure for F` impl that was previously generated inline.
248impl<F> AttrValueAdapter<F>
249where
250 F: FnMut(Event) + 'static,
251{
252 /// Converts the wrapped closure into a callback `AttributeValue`.
253 ///
254 /// # Returns
255 ///
256 /// - `AttributeValue` - An event attribute value wrapping the adapted closure.
257 pub fn into_callback_attribute_value(self) -> AttributeValue {
258 self.into_inner().into_callback_attribute()
259 }
260
261 /// Converts the wrapped closure into a callback `AttributeValue` with a
262 /// custom event name for component props.
263 ///
264 /// # Arguments
265 ///
266 /// - `String` - The custom attribute name (e.g., "on-increment", "on-change").
267 ///
268 /// # Returns
269 ///
270 /// - `AttributeValue` - An event attribute value with the custom name.
271 pub fn into_callback_attribute_value_with_name(self, name: String) -> AttributeValue {
272 AttributeValue::Event(NativeEventHandler::create(
273 NativeEventName::Other(name),
274 self.into_inner(),
275 ))
276 }
277}
278
279/// Adapts an owned `NativeEventHandler` into an `AttributeValue::Event` directly.
280///
281/// When the user already provides a `NativeEventHandler`, the handler is
282/// re-wrapped with a generic "callback" event name so that subsequent
283/// `EventAdapter::into_attribute` calls can override it with the correct
284/// DOM event type. This replaces the `__IsClosure for NativeEventHandler`
285/// impl that was previously generated inline by the `html!` macro.
286impl AttrValueAdapter<NativeEventHandler> {
287 /// Converts the wrapped handler into an event `AttributeValue`.
288 ///
289 /// Re-wraps the handler with a generic callback event name so that the
290 /// DOM event type can be correctly resolved when the handler is later
291 /// bound to a real element via `EventAdapter::into_attribute`.
292 ///
293 /// # Returns
294 ///
295 /// - `AttributeValue` - An `AttributeValue::Event` containing the re-wrapped handler.
296 pub fn into_callback_attribute_value(self) -> AttributeValue {
297 AttributeValue::Event(self.into_inner())
298 }
299
300 /// Converts the wrapped handler into a callback `AttributeValue` with a
301 /// custom event name for component props.
302 ///
303 /// Re-wraps the handler with the provided custom event name so that
304 /// `try_get_callback` can find it by the matching attribute name.
305 ///
306 /// # Arguments
307 ///
308 /// - `String` - The custom attribute name.
309 ///
310 /// # Returns
311 ///
312 /// - `AttributeValue` - An event attribute value with the custom name.
313 pub fn into_callback_attribute_value_with_name(self, name: String) -> AttributeValue {
314 let mut handler: NativeEventHandler = self.into_inner();
315 handler.set_event_name(name);
316 AttributeValue::Event(handler)
317 }
318}
319
320/// Adapts an `Option<NativeEventHandler>` into an `AttributeValue`.
321///
322/// `Some(handler)` becomes `AttributeValue::Event(handler)` re-wrapped with
323/// a generic callback event name, and `None` becomes `AttributeValue::Text(String::new())`.
324/// This replaces the `__IsClosure for Option<NativeEventHandler>` impl that was
325/// previously generated inline by the `html!` macro.
326impl AttrValueAdapter<Option<NativeEventHandler>> {
327 /// Converts the wrapped optional handler into an attribute value.
328 ///
329 /// Re-wraps a `Some` handler with a generic callback event name so that the
330 /// DOM event type can be correctly resolved when the handler is later bound
331 /// to a real element via `EventAdapter::into_attribute`.
332 ///
333 /// # Returns
334 ///
335 /// - `AttributeValue` - An event attribute if `Some`, otherwise an empty text attribute.
336 pub fn into_callback_attribute_value(self) -> AttributeValue {
337 match self.into_inner() {
338 Some(handler) => AttrValueAdapter::new(handler).into_callback_attribute_value(),
339 None => AttributeValue::Text(String::new()),
340 }
341 }
342
343 /// Converts this optional handler into a callback `AttributeValue` with a
344 /// custom event name for component props.
345 ///
346 /// # Arguments
347 ///
348 /// - `String` - The custom attribute name.
349 ///
350 /// # Returns
351 ///
352 /// - `AttributeValue` - An event attribute with the custom name if `Some`,
353 /// otherwise an empty text attribute.
354 pub fn into_callback_attribute_value_with_name(self, name: String) -> AttributeValue {
355 match self.into_inner() {
356 Some(handler) => {
357 AttrValueAdapter::new(handler).into_callback_attribute_value_with_name(name)
358 }
359 None => AttributeValue::Text(String::new()),
360 }
361 }
362}
363
364/// Adapts any `IntoReactiveValue` type into an `AttributeValue`.
365///
366/// This is the fallback path for non-closure attribute values (strings, signals,
367/// CSS classes, etc.). The value is converted via `IntoReactiveValue::into_reactive_value()`.
368/// This replaces the `__ValuePicker` / `__FallbackHelper` hierarchy that was previously
369/// generated inline by the `html!` macro.
370impl<T> AttrValueAdapter<T>
371where
372 T: IntoReactiveValue,
373{
374 /// Converts the wrapped value into an `AttributeValue` via reactive value adaptation.
375 ///
376 /// # Returns
377 ///
378 /// - `AttributeValue` - The reactive attribute value.
379 pub fn into_reactive_attribute_value(self) -> AttributeValue {
380 self.into_inner().into_reactive_value()
381 }
382}