Skip to main content

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}