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}