euv-example 0.3.6

An example application demonstrating the euv UI framework with reactive signals, custom components, and WebAssembly.
Documentation
use crate::*;

/// Reactive state for an async data fetch feature.
#[derive(Clone, Copy, Data, New)]
pub struct UseFetch {
    /// Whether data is currently being fetched.
    #[get(pub, type(copy))]
    #[set(pub)]
    pub loading: Signal<bool>,
    /// The fetched data content.
    #[get(pub, type(copy))]
    #[set(pub)]
    pub data: Signal<String>,
    /// The error message, empty if no error.
    #[get(pub, type(copy))]
    #[set(pub)]
    pub error: Signal<String>,
}

/// Creates fetch state signals wrapped in a `UseFetch` struct.
///
/// # Returns
///
/// - `UseFetch`: The fetch state containing loading, data, and error signals.
pub fn use_fetch() -> UseFetch {
    UseFetch::new(
        use_signal(|| false),
        use_signal(|| "Click fetch to load data".to_string()),
        use_signal(|| "".to_string()),
    )
}

/// Creates a click event handler that fetches data from httpbin.org.
///
/// Sets loading to true, clears error and data, then spawns an async
/// task that performs the HTTP fetch and updates the state signals.
///
/// # Arguments
///
/// - `UseFetch`: The fetch state.
///
/// # Returns
///
/// - `NativeEventHandler`: A click handler to trigger the fetch.
pub fn fetch_on_fetch(state: UseFetch) -> NativeEventHandler {
    NativeEventHandler::new(NativeEventName::Click, move |_event: NativeEvent| {
        state.loading.set(true);
        state.error.set("".to_string());
        state.data.set("".to_string());
        let data_signal: Signal<String> = state.data;
        let error_signal: Signal<String> = state.error;
        let loading_signal: Signal<bool> = state.loading;
        wasm_bindgen_futures::spawn_local(async move {
            let window: web_sys::Window = web_sys::window().expect("no global window exists");
            let promise: js_sys::Promise = window.fetch_with_str("https://httpbin.org/get");
            let future: wasm_bindgen_futures::JsFuture =
                wasm_bindgen_futures::JsFuture::from(promise);
            match future.await {
                Ok(response) => {
                    let resp: web_sys::Response = response.dyn_into().unwrap();
                    let json_promise: js_sys::Promise = resp.json().unwrap();
                    let json_future: wasm_bindgen_futures::JsFuture =
                        wasm_bindgen_futures::JsFuture::from(json_promise);
                    match json_future.await {
                        Ok(json) => {
                            let json_string: String = js_sys::JSON::stringify(&json)
                                .unwrap()
                                .as_string()
                                .unwrap_or_default();
                            data_signal.set(json_string);
                        }
                        Err(_) => {
                            error_signal.set("Failed to parse JSON".to_string());
                        }
                    }
                }
                Err(_) => {
                    error_signal.set("Network request failed".to_string());
                }
            }
            loading_signal.set(false);
        });
    })
}