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}