leptos_use/use_window_size.rs
1use crate::core::Size;
2use crate::{
3 UseEventListenerOptions, use_event_listener_with_options, use_media_query, use_window,
4};
5use default_struct_builder::DefaultBuilder;
6use leptos::ev::resize;
7use leptos::prelude::*;
8
9/// Reactive window size.
10///
11/// ## Demo
12///
13/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_window_size)
14///
15/// ## Usage
16///
17/// ```
18/// # use leptos::*;
19/// # use leptos_use::{use_window_size, UseWindowSizeReturn};
20/// #
21/// # #[component]
22/// # fn Demo() -> impl IntoView {
23/// let UseWindowSizeReturn { width, height } = use_window_size();
24/// #
25/// # view! { }
26/// # }
27/// ```
28///
29/// ## Server-Side Rendering
30///
31/// > Make sure you follow the [instructions in Server-Side Rendering](https://leptos-use.rs/server_side_rendering.html).
32///
33/// On the server the width and height are always `initial_size` which defaults to
34/// `Size { width: INFINITY, height: INFINITY }`.
35// #[doc(cfg(feature = "use_window_size"))]
36pub fn use_window_size() -> UseWindowSizeReturn {
37 use_window_size_with_options(UseWindowSizeOptions::default())
38}
39
40/// Version of [`fn@crate::use_window_size`] that takes a `UseWindowSizeOptions`. See [`fn@crate::use_window_size`] for how to use.
41// #[doc(cfg(feature = "use_window_size"))]
42pub fn use_window_size_with_options(options: UseWindowSizeOptions) -> UseWindowSizeReturn {
43 let UseWindowSizeOptions {
44 initial_size,
45 listen_orientation,
46 include_scrollbar,
47 measure_type,
48 } = options;
49
50 let (width, set_width) = signal(initial_size.width);
51 let (height, set_height) = signal(initial_size.height);
52
53 let update;
54
55 #[cfg(not(feature = "ssr"))]
56 {
57 update = move || match measure_type {
58 MeasureType::Outer => {
59 set_width.set(
60 window()
61 .outer_width()
62 .expect("failed to get window width")
63 .as_f64()
64 .expect("width is not a f64"),
65 );
66 set_height.set(
67 window()
68 .outer_height()
69 .expect("failed to get window height")
70 .as_f64()
71 .expect("height is not a f64"),
72 );
73 }
74 MeasureType::Inner => {
75 if include_scrollbar {
76 set_width.set(
77 window()
78 .inner_width()
79 .expect("failed to get window width")
80 .as_f64()
81 .expect("width is not a f64"),
82 );
83 set_height.set(
84 window()
85 .inner_height()
86 .expect("failed to get window height")
87 .as_f64()
88 .expect("height is not a f64"),
89 );
90 } else {
91 set_width.set(
92 document()
93 .document_element()
94 .expect("no document element")
95 .client_width() as f64,
96 );
97 set_height.set(
98 document()
99 .document_element()
100 .expect("no document element")
101 .client_height() as f64,
102 );
103 }
104 }
105 };
106 }
107
108 #[cfg(feature = "ssr")]
109 {
110 update = || {};
111
112 let _ = initial_size;
113 let _ = include_scrollbar;
114 let _ = measure_type;
115
116 let _ = set_width;
117 let _ = set_height;
118 }
119
120 update();
121 let _ = use_event_listener_with_options(
122 use_window(),
123 resize,
124 move |_| update(),
125 UseEventListenerOptions::default().passive(true),
126 );
127
128 if listen_orientation {
129 let matches = use_media_query("(orientation: portrait)");
130
131 Effect::new(move |_| {
132 let _ = matches.get();
133
134 update();
135 });
136 }
137
138 UseWindowSizeReturn {
139 width: width.into(),
140 height: height.into(),
141 }
142}
143
144/// Options for [`fn@crate::use_window_size_with_options`].
145// #[doc(cfg(feature = "use_window_size"))]
146#[derive(DefaultBuilder)]
147pub struct UseWindowSizeOptions {
148 /// The initial size before anything is measured (like on the server side).
149 /// Defaults to `Size { width: INFINITY, height: INFINITY }`.
150 initial_size: Size,
151
152 /// Listen to the window ` orientationchange ` event. Defaults to `true`.
153 listen_orientation: bool,
154
155 /// Whether the scrollbar should be included in the width and height
156 /// Only effective when `measure_type` is `MeasureType::Inner`.
157 /// Defaults to `true`.
158 include_scrollbar: bool,
159
160 /// Use `window.innerWidth` or `window.outerWidth`.
161 /// Defaults to `MeasureType::Inner`.
162 measure_type: MeasureType,
163}
164
165/// Type of the `measure_type` option.
166#[derive(Default, Clone, Copy, PartialEq, Eq, Debug)]
167pub enum MeasureType {
168 /// Use `window.innerWidth`
169 #[default]
170 Inner,
171 /// Use `window.outerWidth`
172 Outer,
173}
174
175impl Default for UseWindowSizeOptions {
176 fn default() -> Self {
177 Self {
178 initial_size: Size {
179 width: f64::INFINITY,
180 height: f64::INFINITY,
181 },
182 listen_orientation: true,
183 include_scrollbar: true,
184 measure_type: MeasureType::default(),
185 }
186 }
187}
188
189/// Return type of [`fn@crate::use_window_size`].
190// #[doc(cfg(feature = "use_window_size"))]
191pub struct UseWindowSizeReturn {
192 /// The width of the window.
193 pub width: Signal<f64>,
194 /// The height of the window.
195 pub height: Signal<f64>,
196}