leptos_use/
use_screen_orientation.rs

1use leptos::prelude::*;
2
3/// Reactive [Screen Orientation API](https://developer.mozilla.org/en-US/docs/Web/API/Screen_Orientation_API).
4/// It provides web developers with information about the user's current screen orientation.
5///
6/// ## Demo
7///
8/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_screen_orientation)
9///
10/// ## Usage
11///
12/// ```
13/// # use leptos::prelude::*;
14/// # use leptos_use::{use_screen_orientation, UseScreenOrientationReturn};
15/// #
16/// # #[component]
17/// # fn Demo() -> impl IntoView {
18/// let UseScreenOrientationReturn { orientation, angle, lock_orientation, unlock_orientation } = use_screen_orientation();
19/// #
20/// # view! { }
21/// # }
22/// ```
23///
24/// ## Server-Side Rendering
25///
26/// > Make sure you follow the [instructions in Server-Side Rendering](https://leptos-use.rs/server_side_rendering.html).
27///
28/// On the server `orientation` will always be `ScreenOrientation::PortraitPrimary` and `angle` will always be `0`.
29/// The locking functions will be no-ops.
30// #[doc(cfg(feature = "use_screen_orientation"))]
31pub fn use_screen_orientation() -> UseScreenOrientationReturn<
32    impl Fn(ScreenOrientationLock) + Clone + Send + Sync + 'static,
33    impl Fn() + Clone + Send + Sync + 'static,
34> {
35    #[cfg(feature = "ssr")]
36    {
37        UseScreenOrientationReturn {
38            orientation: Signal::stored(ScreenOrientation::PortraitPrimary),
39            angle: Signal::stored(0),
40            lock_orientation: |_| {},
41            unlock_orientation: || {},
42        }
43    }
44
45    #[cfg(not(feature = "ssr"))]
46    {
47        use std::rc::Rc;
48
49        use crate::{UseEventListenerOptions, sendwrap_fn, use_event_listener_with_options};
50        use leptos::ev::orientationchange;
51
52        let screen_orientation = Rc::new(
53            window()
54                .screen()
55                .expect("screen not available")
56                .orientation(),
57        );
58
59        let (orientation, set_orientation) = signal(
60            screen_orientation
61                .type_()
62                .expect("cannot read screen orientation")
63                .into(),
64        );
65        let (angle, set_angle) = signal(screen_orientation.angle().unwrap_or_default());
66
67        let _ = use_event_listener_with_options(
68            window(),
69            orientationchange,
70            {
71                let screen_orientation = Rc::clone(&screen_orientation);
72
73                move |_| {
74                    set_orientation.set(
75                        screen_orientation
76                            .type_()
77                            .expect("cannot read screen orientation")
78                            .into(),
79                    );
80                    set_angle.set(screen_orientation.angle().unwrap_or_default());
81                }
82            },
83            UseEventListenerOptions::default().passive(true),
84        );
85
86        let lock_orientation = {
87            let screen_orientation = Rc::clone(&screen_orientation);
88            sendwrap_fn!(move |lock: ScreenOrientationLock| {
89                let _ = screen_orientation
90                    .lock(lock.into())
91                    .expect("cannot lock screen orientation");
92            })
93        };
94
95        let unlock_orientation = sendwrap_fn!(move || {
96            screen_orientation
97                .unlock()
98                .expect("cannot unlock screen orientation");
99        });
100
101        UseScreenOrientationReturn {
102            orientation: orientation.into(),
103            angle: angle.into(),
104            lock_orientation,
105            unlock_orientation,
106        }
107    }
108}
109
110/// Return type of [`fn@crate::use_screen_orientation`].
111// #[doc(cfg(feature = "use_screen_orientation"))]
112pub struct UseScreenOrientationReturn<LFn, UFn>
113where
114    LFn: Fn(ScreenOrientationLock) + Clone + Send + Sync + 'static,
115    UFn: Fn() + Clone + Send + Sync + 'static,
116{
117    /// Current screen orientation.
118    pub orientation: Signal<ScreenOrientation>,
119
120    /// The document's current orientation angle.
121    pub angle: Signal<u16>,
122
123    /// Locks the screen orientation to the specified orientation.
124    ///
125    /// Typically orientation locking is only enabled on mobile devices,
126    /// and when the browser context is full screen.
127    pub lock_orientation: LFn,
128
129    /// Unlocks the screen orientation.
130    pub unlock_orientation: UFn,
131}
132
133/// Represents the possible screen orientations. Returned by [`UseScreenOrientationReturn::orientation`].
134#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
135pub enum ScreenOrientation {
136    /// The "primary" portrait mode.
137    /// If the natural orientation is a portrait mode (screen height is greater than width),
138    /// this will be the same as the natural orientation, and correspond to an angle of 0 degrees.
139    /// If the natural orientation is a landscape mode,
140    /// then the user agent can choose either portrait orientation as the `PortraitPrimary` and `PortraitSecondary`;
141    /// one of those will be assigned the angle of 90 degrees and the other will have an angle of 270 degrees.
142    PortraitPrimary,
143    /// The secondary portrait orientation.
144    /// If the natural orientation is a portrait mode, this will have an angle of 180 degrees
145    /// (in other words, the device is upside down relative to its natural orientation).
146    /// If the natural orientation is a landscape mode,
147    /// this can be either orientation as selected by the user agent: whichever was not selected for `PortraitPrimary`.
148    PortraitSecondary,
149    /// The "primary" landscape mode.
150    /// If the natural orientation is a landscape mode (screen width is greater than height),
151    /// this will be the same as the natural orientation, and correspond to an angle of 0 degrees.
152    /// If the natural orientation is a portrait mode,
153    /// then the user agent can choose either landscape orientation as the `landscape-primary`
154    /// with an angle of either 90 or 270 degrees (`LandscapeSecondary` will be the other orientation and angle).
155    LandscapePrimary,
156    /// The secondary landscape mode.
157    /// If the natural orientation is a landscape mode,
158    /// this orientation is upside down relative to the natural orientation, and will have an angle of 180 degrees.
159    /// If the natural orientation is a portrait mode, this can be either orientation as selected by the user agent:
160    /// whichever was not selected for `LandscapePrimary`.
161    LandscapeSecondary,
162}
163
164/// Represents the possible screen orientation lock modes. Passed to [`UseScreenOrientationReturn::lock_orientation`].
165#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
166pub enum ScreenOrientationLock {
167    /// Any of `PortraitPrimary`, `PortraitSecondary`, `LandscapePrimary` or `LandscapeSecondary`.
168    Any,
169    /// The natural orientation of the screen from the underlying operating system: either `PortraitPrimary` or `LandscapePrimary`.
170    Natural,
171    /// An orientation where screen width is greater than the screen height.
172    /// Depending on the platform convention, this may be `LandscapePrimary`, `LandscapeSecondary`, or both.
173    Landscape,
174    /// An orientation where screen height is greater than the screen width.
175    /// Depending on the platform convention, this may be `PortraitPrimary`, `PortraitSecondary`, or both.
176    Portrait,
177    /// The "primary" portrait mode.
178    /// If the natural orientation is a portrait mode (screen height is greater than width),
179    /// this will be the same as the natural orientation, and correspond to an angle of 0 degrees.
180    /// If the natural orientation is a landscape mode,
181    /// then the user agent can choose either portrait orientation as the `PortraitPrimary` and `PortraitSecondary`;
182    /// one of those will be assigned the angle of 90 degrees and the other will have an angle of 270 degrees.
183    PortraitPrimary,
184    /// The secondary portrait orientation.
185    /// If the natural orientation is a portrait mode, this will have an angle of 180 degrees
186    /// (in other words, the device is upside down relative to its natural orientation).
187    /// If the natural orientation is a landscape mode,
188    /// this can be either orientation as selected by the user agent: whichever was not selected for `PortraitPrimary`.
189    PortraitSecondary,
190    /// The "primary" landscape mode.
191    /// If the natural orientation is a landscape mode (screen width is greater than height),
192    /// this will be the same as the natural orientation, and correspond to an angle of 0 degrees.
193    /// If the natural orientation is a portrait mode,
194    /// then the user agent can choose either landscape orientation as the `landscape-primary`
195    /// with an angle of either 90 or 270 degrees (`LandscapeSecondary` will be the other orientation and angle).
196    LandscapePrimary,
197    /// The secondary landscape mode.
198    /// If the natural orientation is a landscape mode,
199    /// this orientation is upside down relative to the natural orientation, and will have an angle of 180 degrees.
200    /// If the natural orientation is a portrait mode, this can be either orientation as selected by the user agent:
201    /// whichever was not selected for `LandscapePrimary`.
202    LandscapeSecondary,
203}
204
205#[cfg(not(feature = "ssr"))]
206impl From<web_sys::OrientationType> for ScreenOrientation {
207    fn from(value: web_sys::OrientationType) -> Self {
208        match value {
209            web_sys::OrientationType::PortraitPrimary => ScreenOrientation::PortraitPrimary,
210            web_sys::OrientationType::PortraitSecondary => ScreenOrientation::PortraitSecondary,
211            web_sys::OrientationType::LandscapePrimary => ScreenOrientation::LandscapePrimary,
212            web_sys::OrientationType::LandscapeSecondary => ScreenOrientation::LandscapeSecondary,
213            _ => unreachable!(),
214        }
215    }
216}
217
218#[cfg(not(feature = "ssr"))]
219impl From<ScreenOrientation> for web_sys::OrientationType {
220    fn from(value: ScreenOrientation) -> Self {
221        match value {
222            ScreenOrientation::PortraitPrimary => web_sys::OrientationType::PortraitPrimary,
223            ScreenOrientation::PortraitSecondary => web_sys::OrientationType::PortraitSecondary,
224            ScreenOrientation::LandscapePrimary => web_sys::OrientationType::LandscapePrimary,
225            ScreenOrientation::LandscapeSecondary => web_sys::OrientationType::LandscapeSecondary,
226        }
227    }
228}
229
230#[cfg(not(feature = "ssr"))]
231impl From<web_sys::OrientationLockType> for ScreenOrientationLock {
232    fn from(value: web_sys::OrientationLockType) -> Self {
233        match value {
234            web_sys::OrientationLockType::Any => ScreenOrientationLock::Any,
235            web_sys::OrientationLockType::Natural => ScreenOrientationLock::Natural,
236            web_sys::OrientationLockType::Landscape => ScreenOrientationLock::Landscape,
237            web_sys::OrientationLockType::Portrait => ScreenOrientationLock::Portrait,
238            web_sys::OrientationLockType::PortraitPrimary => ScreenOrientationLock::PortraitPrimary,
239            web_sys::OrientationLockType::PortraitSecondary => {
240                ScreenOrientationLock::PortraitSecondary
241            }
242            web_sys::OrientationLockType::LandscapePrimary => {
243                ScreenOrientationLock::LandscapePrimary
244            }
245            web_sys::OrientationLockType::LandscapeSecondary => {
246                ScreenOrientationLock::LandscapeSecondary
247            }
248            _ => unreachable!(),
249        }
250    }
251}
252
253#[cfg(not(feature = "ssr"))]
254impl From<ScreenOrientationLock> for web_sys::OrientationLockType {
255    fn from(value: ScreenOrientationLock) -> Self {
256        match value {
257            ScreenOrientationLock::Any => web_sys::OrientationLockType::Any,
258            ScreenOrientationLock::Natural => web_sys::OrientationLockType::Natural,
259            ScreenOrientationLock::Landscape => web_sys::OrientationLockType::Landscape,
260            ScreenOrientationLock::Portrait => web_sys::OrientationLockType::Portrait,
261            ScreenOrientationLock::PortraitPrimary => web_sys::OrientationLockType::PortraitPrimary,
262            ScreenOrientationLock::PortraitSecondary => {
263                web_sys::OrientationLockType::PortraitSecondary
264            }
265            ScreenOrientationLock::LandscapePrimary => {
266                web_sys::OrientationLockType::LandscapePrimary
267            }
268            ScreenOrientationLock::LandscapeSecondary => {
269                web_sys::OrientationLockType::LandscapeSecondary
270            }
271        }
272    }
273}