@@ -7,7 +7,11 @@ use gloo_net::http::Request;
77use leptos:: callback:: Callback ;
88use leptos:: task:: spawn_local;
99use leptos:: { html:: * , prelude:: * , * } ;
10- use web_sys:: RequestCache ;
10+ use web_sys:: IntersectionObserverEntry ;
11+ use web_sys:: js_sys;
12+ use web_sys:: wasm_bindgen:: JsCast ;
13+ use web_sys:: wasm_bindgen:: closure:: Closure ;
14+ use web_sys:: { IntersectionObserver , IntersectionObserverInit , RequestCache } ;
1115
1216// Comment out aria attrs cause of: tachys-0.2.0/src/html/attribute/mod.rs:593:1:
1317// not yet implemented: adding more than 26 attributes is not supported
@@ -141,6 +145,46 @@ pub fn Image(
141145) -> impl IntoView {
142146 let ( img_src, set_img_src) = signal ( src) ;
143147
148+ Effect :: new ( move || {
149+ let callback = Closure :: wrap ( Box :: new (
150+ move |entries : js_sys:: Array , _observer : IntersectionObserver | {
151+ if let Some ( entry) = entries. get ( 0 ) . dyn_ref :: < IntersectionObserverEntry > ( ) {
152+ if entry. is_intersecting ( ) {
153+ if let Some ( node) = node_ref. get ( ) {
154+ if let Some ( img) = node. dyn_ref :: < web_sys:: HtmlImageElement > ( ) {
155+ img. set_src ( src) ;
156+ if let Some ( cb) = on_load {
157+ cb. run ( ( ) ) ;
158+ }
159+ }
160+ }
161+ }
162+ }
163+ } ,
164+ )
165+ as Box < dyn FnMut ( js_sys:: Array , IntersectionObserver ) > ) ;
166+
167+ let options = IntersectionObserverInit :: new ( ) ;
168+ options. set_threshold ( & js_sys:: Array :: of1 ( & 0.1 . into ( ) ) ) ;
169+
170+ let observer =
171+ IntersectionObserver :: new_with_options ( callback. as_ref ( ) . unchecked_ref ( ) , & options)
172+ . expect ( "Failed to create IntersectionObserver" ) ;
173+
174+ if let Some ( element) = node_ref. get ( ) {
175+ if let Ok ( img) = element. clone ( ) . dyn_into :: < web_sys:: HtmlElement > ( ) {
176+ observer. observe ( & img) ;
177+ }
178+ }
179+
180+ let observer_clone = observer. clone ( ) ;
181+ let _cleanup = move || {
182+ observer_clone. disconnect ( ) ;
183+ } ;
184+
185+ callback. forget ( ) ;
186+ } ) ;
187+
144188 let onload = move |_| {
145189 if let Some ( cb) = on_load {
146190 cb. run ( ( ) ) ;
@@ -247,6 +291,7 @@ pub fn Image(
247291 </span>
248292 }
249293 . into_any ( ) ,
294+
250295 Layout :: Responsive => {
251296 let ratio = height. parse :: < f64 > ( ) . unwrap_or ( 1.0 ) / width. parse :: < f64 > ( ) . unwrap_or ( 1.0 ) ;
252297 let padding = format ! ( "{}%" , ratio * 100.0 ) ;
@@ -291,7 +336,55 @@ pub fn Image(
291336 }
292337 . into_any ( )
293338 }
294- _ => view ! {
339+
340+ Layout :: Intrinsic => view ! {
341+ <span style="display:inline-block; position:relative; max-width:100%;" >
342+ <span style="max-width:100%;" >
343+ <img
344+ node_ref=node_ref
345+ src=move || img_src. get( )
346+ alt=alt
347+ class=class
348+ width=width
349+ height=height
350+ style=full_style. clone( )
351+ sizes=sizes
352+ srcset=srcset
353+ decoding=decoding. as_str( )
354+ crossorigin=crossorigin. as_str( )
355+ referrerpolicy=referrerpolicy. as_str( )
356+ loading=loading. as_str( )
357+ fetchpriority=fetchpriority. as_str( )
358+ aria_placeholder=placeholder
359+ on: load=onload
360+ on: error=onerror
361+ role="img"
362+ // aria-label=alt
363+ // aria-labelledby=aria_labelledby
364+ // aria-describedby=aria_describedby
365+ // aria-hidden=aria_hidden
366+ // aria-current=aria_current
367+ // aria-expanded=aria_expanded
368+ // aria-live=aria_live.as_str()
369+ // aria-pressed=aria_pressed.as_str()
370+ // aria-controls=aria_controls
371+ usemap=usemap
372+ ismap=ismap
373+ elementtiming=elementtiming
374+ attributionsrc=attributionsrc
375+ />
376+ </span>
377+ <img
378+ src=blur_data_url
379+ style="display:none;"
380+ alt=alt
381+ aria-hidden="true"
382+ />
383+ </span>
384+ }
385+ . into_any ( ) ,
386+
387+ Layout :: Fixed => view ! {
295388 <span style="display:inline-block; position:relative;" >
296389 <img
297390 node_ref=node_ref
@@ -329,6 +422,123 @@ pub fn Image(
329422 </span>
330423 }
331424 . into_any ( ) ,
425+
426+ Layout :: Auto => view ! {
427+ <span style="display:inline-block; position:relative;" >
428+ <img
429+ node_ref=node_ref
430+ src=move || img_src. get( )
431+ alt=alt
432+ class=class
433+ width=width
434+ height=height
435+ style=full_style. clone( )
436+ sizes=sizes
437+ srcset=srcset
438+ decoding=decoding. as_str( )
439+ crossorigin=crossorigin. as_str( )
440+ referrerpolicy=referrerpolicy. as_str( )
441+ loading=loading. as_str( )
442+ fetchpriority=fetchpriority. as_str( )
443+ aria_placeholder=placeholder
444+ on: load=onload
445+ on: error=onerror
446+ role="img"
447+ // aria-label=alt
448+ // aria-labelledby=aria_labelledby
449+ // aria-describedby=aria_describedby
450+ // aria-hidden=aria_hidden
451+ // aria-current=aria_current
452+ // aria-expanded=aria_expanded
453+ // aria-live=aria_live.as_str()
454+ // aria-pressed=aria_pressed.as_str()
455+ // aria-controls=aria_controls
456+ usemap=usemap
457+ ismap=ismap
458+ elementtiming=elementtiming
459+ attributionsrc=attributionsrc
460+ />
461+ </span>
462+ }
463+ . into_any ( ) ,
464+
465+ Layout :: Stretch => view ! {
466+ <span style="display:block; width:100%; height:100%; position:relative;" >
467+ <img
468+ node_ref=node_ref
469+ src=move || img_src. get( )
470+ alt=alt
471+ class=class
472+ width="100%"
473+ height="100%"
474+ style=full_style. clone( )
475+ sizes=sizes
476+ srcset=srcset
477+ decoding=decoding. as_str( )
478+ crossorigin=crossorigin. as_str( )
479+ referrerpolicy=referrerpolicy. as_str( )
480+ loading=loading. as_str( )
481+ fetchpriority=fetchpriority. as_str( )
482+ aria_placeholder=placeholder
483+ on: load=onload
484+ on: error=onerror
485+ role="img"
486+ // aria-label=alt
487+ // aria-labelledby=aria_labelledby
488+ // aria-describedby=aria_describedby
489+ // aria-hidden=aria_hidden
490+ // aria-current=aria_current
491+ // aria-expanded=aria_expanded
492+ // aria-live=aria_live.as_str()
493+ // aria-pressed=aria_pressed.as_str()
494+ // aria-controls=aria_controls
495+ usemap=usemap
496+ ismap=ismap
497+ elementtiming=elementtiming
498+ attributionsrc=attributionsrc
499+ />
500+ </span>
501+ }
502+ . into_any ( ) ,
503+
504+ Layout :: ScaleDown => view ! {
505+ <span style="display:inline-block; position:relative; max-width:100%; max-height:100%;" >
506+ <img
507+ node_ref=node_ref
508+ src=move || img_src. get( )
509+ alt=alt
510+ class=class
511+ width=width
512+ height=height
513+ style=full_style. clone( )
514+ sizes=sizes
515+ srcset=srcset
516+ decoding=decoding. as_str( )
517+ crossorigin=crossorigin. as_str( )
518+ referrerpolicy=referrerpolicy. as_str( )
519+ loading=loading. as_str( )
520+ fetchpriority=fetchpriority. as_str( )
521+ aria_placeholder=placeholder
522+ on: load=onload
523+ on: error=onerror
524+ role="img"
525+ // aria-label=alt
526+ // aria-labelledby=aria_labelledby
527+ // aria-describedby=aria_describedby
528+ // aria-hidden=aria_hidden
529+ // aria-current=aria_current
530+ // aria-expanded=aria_expanded
531+ // aria-live=aria_live.as_str()
532+ // aria-pressed=aria_pressed.as_str()
533+ // aria-controls=aria_controls
534+ usemap=usemap
535+ ismap=ismap
536+ elementtiming=elementtiming
537+ attributionsrc=attributionsrc
538+ />
539+ </span>
540+ }
541+ . into_any ( ) ,
332542 } ;
333543
334544 view ! {
0 commit comments