yew_more_hooks/hooks/breakpoint.rs
1use yew::prelude::*;
2use yew_hooks::use_event_with_window;
3
4/// A trait characterizing breakpoints for viewport sizes.
5///
6/// ## Example
7///
8/// ```
9/// use yew_more_hooks::prelude::Breakpoint;
10///
11/// #[derive(PartialEq, Clone, Copy)]
12/// enum AppBreakpoint {
13/// Small,
14/// Large,
15/// }
16///
17/// impl Breakpoint for AppBreakpoint {
18/// fn from_screen_width(pixels: usize) -> Self {
19/// if pixels < Self::Large.as_pixels() {
20/// Self::Small
21/// } else {
22/// Self::Large
23/// }
24/// }
25///
26/// fn as_pixels(&self) -> usize {
27/// match self {
28/// Self::Small => 0,
29/// Self::Large => 1280,
30/// }
31/// }
32/// }
33/// ```
34pub trait Breakpoint: PartialEq + Sized {
35 fn as_pixels(&self) -> usize;
36
37 fn from_screen_width(pixels: usize) -> Self;
38
39 fn current() -> Self {
40 let width = web_sys::window()
41 .expect("Couldn't get window")
42 .inner_width()
43 .expect("Couldn't retrieve width of window")
44 .as_f64()
45 .expect("Couldn't convert window size to number")
46 .round();
47 Self::from_screen_width(width as usize)
48 }
49}
50
51/// Causes a rerender of a component when the viewport width is changed beyond a breakpoint.
52///
53/// ## Example
54/// ```
55/// # use yew_more_hooks::prelude::Breakpoint;
56/// use yew_more_hooks::prelude::use_breakpoint;
57/// use yew::prelude::*;
58/// #
59/// # #[derive(PartialEq, Clone, Copy)]
60/// # enum AppBreakpoint {
61/// # Small,
62/// # Large,
63/// # }
64/// #
65/// # impl Breakpoint for AppBreakpoint {
66/// # fn from_screen_width(pixels: usize) -> Self {
67/// # if pixels < Self::Large.as_pixels() {
68/// # Self::Small
69/// # } else {
70/// # Self::Large
71/// # }
72/// # }
73/// #
74/// # fn as_pixels(&self) -> usize {
75/// # match self {
76/// # Self::Small => 0,
77/// # Self::Large => 1280,
78/// # }
79/// # }
80/// # }
81///
82/// #[function_component(Example)]
83/// fn example() -> Html {
84/// let breakpoint = use_breakpoint::<AppBreakpoint>();
85/// let label = use_memo(breakpoint.clone(), |breakpoint| match **breakpoint {
86/// AppBreakpoint::Small => html!("Label"),
87/// AppBreakpoint::Large => html!("A very long descriptive label"),
88/// });
89/// (*label).clone()
90/// }
91/// ```
92#[hook]
93pub fn use_breakpoint<T: Breakpoint + 'static>() -> UseStateHandle<T> {
94 let state: UseStateHandle<T> = use_state_eq(Breakpoint::current);
95 {
96 let state = state.clone();
97 use_event_with_window("resize", move |_: Event| {
98 state.set(Breakpoint::current());
99 });
100 }
101 state.clone()
102}