euv_core/app/impl.rs
1use crate::*;
2
3/// Implementation of core framework APIs for the `App` struct.
4///
5/// This implementation block provides static methods that delegate to the
6/// corresponding framework functions. All methods are available directly
7/// on the `App` type without requiring an instance.
8impl App {
9 /// Creates a new reactive signal with the given initial value.
10 ///
11 /// Uses the current `HookContext` to maintain signal identity across
12 /// re-renders. On the first call at a given hook index, the signal
13 /// is created with `init()` and stored. On subsequent re-renders,
14 /// the existing signal at that index is returned unchanged.
15 ///
16 /// # Arguments
17 ///
18 /// - `FnOnce() -> T` - A closure that computes the initial value of the signal.
19 ///
20 /// # Returns
21 ///
22 /// - `Signal<T>` - A reactive signal containing the initialized or existing value.
23 pub fn use_signal<T, F>(init: F) -> Signal<T>
24 where
25 T: Clone + PartialEq + 'static,
26 F: FnOnce() -> T,
27 {
28 HookContext::signal(init)
29 }
30
31 /// Batches signal updates within a closure, deferring DOM dispatch until the
32 /// outermost batch completes.
33 ///
34 /// Sets `SUPPRESS_SCHEDULE` to `true` so that any `Signal::set()` calls
35 /// inside the closure mark their dependents dirty precisely but do not
36 /// queue a microtask dispatch. When the outermost batch completes,
37 /// a single dispatch is scheduled if any dirty slots were accumulated
38 /// during the batch, ensuring that all pending updates are processed.
39 ///
40 /// Unlike the legacy full-broadcast approach, this uses precise dependency
41 /// tracking: only the dynamic nodes that actually depend on the changed
42 /// signals are marked dirty and re-rendered.
43 ///
44 /// # Arguments
45 ///
46 /// - `FnOnce() -> R` - The closure to execute with batched updates.
47 ///
48 /// # Returns
49 ///
50 /// - `R` - The result of the closure execution.
51 pub fn batch<F, R>(callback: F) -> R
52 where
53 F: FnOnce() -> R,
54 {
55 Scheduler::batch(callback)
56 }
57
58 /// Mounts the given virtual DOM tree to a specific element matched by a CSS selector.
59 ///
60 /// Supported selector syntax:
61 /// - `"#id"` — select by element ID
62 /// - `".class"` — select by class name (uses the first match)
63 /// - `"tag"` — select by tag name (uses the first match)
64 ///
65 /// # Arguments
66 ///
67 /// - `S: AsRef<str>` - A CSS selector string to locate the target element.
68 /// - `FnOnce() -> VirtualNode + 'static` - A closure that returns the virtual DOM tree to render.
69 pub fn mount<S, F>(selector: S, render_fn: F)
70 where
71 S: AsRef<str>,
72 F: FnOnce() -> VirtualNode,
73 {
74 Mount::setup(selector, render_fn)
75 }
76
77 /// Schedules a deferred signal update with precise dirty marking.
78 ///
79 /// Marks only the specified dynamic node IDs as dirty, then queues a
80 /// single microtask dispatch if one is not already pending. When
81 /// `SUPPRESS_SCHEDULE` is `true`, slots are still marked dirty but no
82 /// dispatch is scheduled, allowing `batch` to batch
83 /// precise dirty marks without triggering premature DOM updates.
84 ///
85 /// # Arguments
86 ///
87 /// - `&[usize]` - Dynamic node IDs to mark dirty.
88 pub fn schedule_update(dependents: &[usize]) {
89 Scheduler::update(dependents)
90 }
91
92 /// Registers a cleanup callback that will be executed when the current
93 /// hook context is cleared (e.g., when a `match` arm switches).
94 ///
95 /// This is useful for cleaning up side effects like intervals, timeouts,
96 /// or subscriptions that are not automatically managed by signals.
97 ///
98 /// The cleanup callback is only registered once on the first render.
99 /// On subsequent re-renders at the same hook index, this is a no-op.
100 ///
101 /// # Arguments
102 ///
103 /// - `FnOnce() + 'static` - The cleanup callback to execute on context teardown.
104 pub fn use_cleanup<F>(cleanup: F)
105 where
106 F: FnOnce() + 'static,
107 {
108 HookContext::cleanup(cleanup)
109 }
110
111 /// Creates a recurring interval that invokes the given closure at the
112 /// specified period, returning an `IntervalHandle` that is automatically
113 /// cleared when the hook context is cleared (i.e., when the component
114 /// unmounts or a `match` arm switches).
115 ///
116 /// Unlike calling `set_interval_with_callback_and_timeout_and_arguments_0`
117 /// + `Closure::forget()` manually, this hook ensures the interval is
118 /// properly cleaned up, preventing memory leaks and stale callbacks.
119 ///
120 /// The interval is only created once on the first render.
121 /// On subsequent re-renders at the same hook index, the existing handle
122 /// is returned unchanged.
123 ///
124 /// # Arguments
125 ///
126 /// - `i32` - The interval period in milliseconds.
127 /// - `FnMut() + 'static` - The closure to invoke on each interval tick.
128 ///
129 /// # Returns
130 ///
131 /// - `IntervalHandle` - A handle that can be used to cancel the interval early.
132 ///
133 /// # Panics
134 ///
135 /// Panics if `window()` is unavailable on the current platform.
136 pub fn use_interval<F>(millis: i32, callback: F) -> IntervalHandle
137 where
138 F: FnMut() + 'static,
139 {
140 HookContext::interval(millis, callback)
141 }
142
143 /// Registers a `window.addEventListener` callback using event delegation,
144 /// automatically removed when the hook context is cleared.
145 ///
146 /// Uses the global window event proxy registry so that only one
147 /// `window.addEventListener` call is made per event name regardless of
148 /// how many components listen to the same event. On cleanup, only the
149 /// handler entry is removed from the proxy registry; the shared window
150 /// listener remains active for other consumers.
151 ///
152 /// The event listener is only registered once on the first render.
153 /// On subsequent re-renders at the same hook index, this is a no-op.
154 ///
155 /// # Arguments
156 ///
157 /// - `E: AsRef<str>` - The event name to listen for (e.g., "hashchange", "popstate", "resize").
158 /// - `FnMut() + 'static` - The callback to invoke when the event fires.
159 pub fn use_window_event<E, F>(event_name: E, callback: F)
160 where
161 E: AsRef<str>,
162 F: FnMut() + 'static,
163 {
164 HookContext::window_event(event_name, callback)
165 }
166}