1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
use crate::prelude::*;
use gloo_net::http::Request;
use wasm_bindgen_futures::spawn_local;
use web_sys::RequestCache;

/// Properties for the Image component.
#[derive(Properties, Clone, PartialEq)]
pub struct ImageProps {
    /// The source URL for the image.
    pub src: &'static str,

    /// The alternative text for the image.
    pub alt: &'static str,

    /// The width of the image.
    pub width: &'static str,

    /// The height of the image.
    pub height: &'static str,

    // Common props
    #[prop_or_default]
    /// The style attribute for the image.
    pub style: &'static str,

    #[prop_or_default]
    /// The CSS class for the image.
    pub class: &'static str,

    #[prop_or_default]
    /// The sizes attribute for the image.
    pub sizes: &'static str,

    #[prop_or_default]
    /// The quality attribute for the image.
    pub quality: &'static str,

    #[prop_or_default]
    /// Indicates if the image should have priority loading.
    pub priority: bool,

    #[prop_or_default]
    /// The placeholder attribute for the image.
    pub placeholder: &'static str,

    #[prop_or_default]
    /// Callback function for handling loading completion.
    pub on_loading_complete: Callback<()>,

    // Advanced Props
    #[prop_or_default]
    /// The object-fit attribute for the image.
    pub object_fit: &'static str,

    #[prop_or_default]
    /// The object-position attribute for the image.
    pub object_position: &'static str,

    #[prop_or_default]
    /// Callback function for handling errors during image loading.
    pub on_error: Callback<String>,

    #[prop_or_default]
    /// The decoding attribute for the image.
    pub decoding: &'static str,

    #[prop_or_default]
    /// The blur data URL for placeholder image.
    pub blur_data_url: &'static str,

    #[prop_or_default]
    /// The lazy boundary for lazy loading.
    pub lazy_boundary: &'static str,

    #[prop_or_default]
    /// Indicates if the image should be unoptimized.
    pub unoptimized: bool,

    // Other Props
    #[prop_or_default]
    /// Reference to the DOM node.
    pub node_ref: NodeRef,
}

/// The Image component for displaying images with various options.
///
/// # Arguments
/// * `props` - The properties of the component.
///
/// # Returns
/// (Html): An HTML representation of the image component.
///
/// # Examples
/// ```
/// // Example of using the Image component
/// use next_rs::{Image, ImageProps};
/// use next_rs::prelude::*;
///
/// #[function_component(MyComponent)]
/// pub fn my_component() -> Html {
///     let image_props = ImageProps {
///         src: "images/logo.png",
///         alt: "Example Image",
///         width: "200",
///         height: "300",
///         style: "border: 1px solid #ddd;",
///         class: "image-class",
///         sizes: "(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw",
///         quality: "80",
///         priority: true,
///         placeholder: "blur",
///         on_loading_complete: Callback::from(|_| {
///             println!("Image loading is complete!");
///         }),
///         object_fit: "cover",
///         object_position: "center",
///         on_error: Callback::from(|err| {
///             println!("Error loading image: {:?}", err);
///         }),
///         decoding: "async",
///         blur_data_url: "data:image/png;base64,....",
///         lazy_boundary: "200px",
///         unoptimized: false,
///         node_ref: NodeRef::default(),
///     };
///
///     html! {
///         <Image ..image_props />
///     }
/// }
/// ```
#[function_component(Image)]
pub fn image(props: &ImageProps) -> Html {
    let props = props.clone();

    let fetch_data = {
        Callback::from(move |_| {
            let loader_url = props.src;
            let loading_complete_callback = props.on_loading_complete.clone();
            let on_error_callback = props.on_error.clone();
            spawn_local(async move {
                match Request::get(&loader_url)
                    .cache(RequestCache::Reload)
                    .send()
                    .await
                {
                    Ok(response) => {
                        let json_result = response.json::<serde_json::Value>();
                        match json_result.await {
                            Ok(_data) => {
                                loading_complete_callback.emit(());
                            }
                            Err(_err) => {
                                let event = Event::new("error").unwrap();
                                on_error_callback.emit(event.to_string().into());
                            }
                        }
                    }
                    Err(_err) => {
                        let event = Event::new("error").unwrap();
                        on_error_callback.emit(event.to_string().into());
                    }
                }
            });
        })
    };

    html! {
        <img
            src={props.src}
            alt={props.alt}
            width={props.width}
            height={props.height}
            style={props.style}
            class={props.class}
            loading={if props.priority { "eager" } else { "lazy" }}
            sizes={props.sizes}
            quality={props.quality}
            placeholder={props.placeholder}
            object-fit={props.object_fit}
            object-position={props.object_position}
            onerror={fetch_data.clone()}
            decoding={props.decoding}
            ref={props.node_ref}
        />
    }
}