leptos_use/
use_element_size.rs1use crate::core::IntoElementMaybeSignal;
2use crate::core::Size;
3use cfg_if::cfg_if;
4use default_struct_builder::DefaultBuilder;
5use leptos::prelude::*;
6use leptos::reactive::wrappers::read::Signal;
7
8cfg_if! { if #[cfg(not(feature = "ssr"))] {
9 use crate::{use_resize_observer_with_options, UseResizeObserverOptions};
10 use crate::{watch_with_options, WatchOptions};
11 use wasm_bindgen::JsCast;
12}}
13
14pub fn use_element_size<El, M>(target: El) -> UseElementSizeReturn
52where
53 El: IntoElementMaybeSignal<web_sys::Element, M>,
54{
55 use_element_size_with_options(target, UseElementSizeOptions::default())
56}
57
58#[cfg_attr(feature = "ssr", allow(unused_variables))]
60pub fn use_element_size_with_options<El, M>(
61 target: El,
62 options: UseElementSizeOptions,
63) -> UseElementSizeReturn
64where
65 El: IntoElementMaybeSignal<web_sys::Element, M>,
66{
67 let UseElementSizeOptions { box_, initial_size } = options;
68
69 let (width, set_width) = signal(initial_size.width);
70 let (height, set_height) = signal(initial_size.height);
71
72 #[cfg(not(feature = "ssr"))]
73 {
74 let box_ = box_.unwrap_or(web_sys::ResizeObserverBoxOptions::ContentBox);
75
76 let target = target.into_element_maybe_signal();
77
78 let is_svg = move || {
79 if let Some(target) = target.get_untracked() {
80 target
81 .namespace_uri()
82 .map(|ns| ns.contains("svg"))
83 .unwrap_or(false)
84 } else {
85 false
86 }
87 };
88
89 let _ = use_resize_observer_with_options(
90 target,
91 move |entries, _| {
92 let entry = &entries[0];
93
94 let box_size = match box_ {
95 web_sys::ResizeObserverBoxOptions::ContentBox => entry.content_box_size(),
96 web_sys::ResizeObserverBoxOptions::BorderBox => entry.border_box_size(),
97 web_sys::ResizeObserverBoxOptions::DevicePixelContentBox => {
98 entry.device_pixel_content_box_size()
99 }
100 _ => unreachable!(),
101 };
102
103 if is_svg() {
104 if let Some(target) = target.get() {
105 if let Ok(Some(styles)) = window().get_computed_style(&target) {
106 set_height.set(
107 styles
108 .get_property_value("height")
109 .map(|v| v.parse().unwrap_or_default())
110 .unwrap_or_default(),
111 );
112 set_width.set(
113 styles
114 .get_property_value("width")
115 .map(|v| v.parse().unwrap_or_default())
116 .unwrap_or_default(),
117 );
118 }
119 }
120 } else if !box_size.is_null() && !box_size.is_undefined() && box_size.length() > 0 {
121 let format_box_size = if box_size.is_array() {
122 box_size.to_vec()
123 } else {
124 vec![box_size.into()]
125 };
126
127 set_width.set(format_box_size.iter().fold(0.0, |acc, v| {
128 acc + v
129 .as_ref()
130 .clone()
131 .unchecked_into::<web_sys::ResizeObserverSize>()
132 .inline_size()
133 }));
134 set_height.set(format_box_size.iter().fold(0.0, |acc, v| {
135 acc + v
136 .as_ref()
137 .clone()
138 .unchecked_into::<web_sys::ResizeObserverSize>()
139 .block_size()
140 }))
141 } else {
142 set_width.set(entry.content_rect().width());
144 set_height.set(entry.content_rect().height())
145 }
146 },
147 UseResizeObserverOptions::default().box_(box_),
148 );
149
150 let _ = watch_with_options(
151 move || target.get(),
152 move |ele, _, _| {
153 if ele.is_some() {
154 set_width.set(initial_size.width);
155 set_height.set(initial_size.height);
156 } else {
157 set_width.set(0.0);
158 set_height.set(0.0);
159 }
160 },
161 WatchOptions::default().immediate(false),
162 );
163 }
164
165 UseElementSizeReturn {
166 width: width.into(),
167 height: height.into(),
168 }
169}
170
171#[derive(DefaultBuilder, Default)]
172pub struct UseElementSizeOptions {
174 initial_size: Size,
177
178 #[builder(into)]
180 pub box_: Option<web_sys::ResizeObserverBoxOptions>,
181}
182
183pub struct UseElementSizeReturn {
185 pub width: Signal<f64>,
187 pub height: Signal<f64>,
189}