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