yew_bs/components/
offcanvas.rs

1use yew::prelude::*;
2use web_sys::Element;
3use wasm_bindgen::JsValue;
4use crate::interop::BsOffcanvas;
5#[derive(Clone, Copy, PartialEq, Debug)]
6pub enum OffcanvasPlacement {
7    Start,
8    End,
9    Top,
10    Bottom,
11}
12impl OffcanvasPlacement {
13    pub fn as_str(&self) -> &'static str {
14        match self {
15            OffcanvasPlacement::Start => "start",
16            OffcanvasPlacement::End => "end",
17            OffcanvasPlacement::Top => "top",
18            OffcanvasPlacement::Bottom => "bottom",
19        }
20    }
21}
22/// Props for the Offcanvas component
23#[derive(Properties, PartialEq)]
24pub struct OffcanvasProps {
25    /// Offcanvas children
26    #[prop_or_default]
27    pub children: Children,
28    /// Whether the offcanvas is shown
29    #[prop_or_default]
30    pub show: bool,
31    /// Offcanvas placement
32    #[prop_or(OffcanvasPlacement::End)]
33    pub placement: OffcanvasPlacement,
34    /// Whether to show backdrop
35    #[prop_or(true)]
36    pub backdrop: bool,
37    /// Whether to allow backdrop click to close
38    #[prop_or(true)]
39    pub backdrop_close: bool,
40    /// Whether to enable keyboard escape
41    #[prop_or(true)]
42    pub keyboard: bool,
43    /// Whether to enable body scroll
44    #[prop_or(false)]
45    pub scroll: bool,
46    /// Callback when offcanvas is shown
47    #[prop_or_default]
48    pub on_show: Option<Callback<()>>,
49    /// Callback when offcanvas is hidden
50    #[prop_or_default]
51    pub on_hide: Option<Callback<()>>,
52    /// Callback when offcanvas show is complete
53    #[prop_or_default]
54    pub on_shown: Option<Callback<()>>,
55    /// Callback when offcanvas hide is complete
56    #[prop_or_default]
57    pub on_hidden: Option<Callback<()>>,
58    /// Additional CSS classes
59    #[prop_or_default]
60    pub class: Option<AttrValue>,
61    /// Additional HTML attributes
62    #[prop_or_default]
63    pub node_ref: NodeRef,
64}
65/// Bootstrap Offcanvas component
66#[function_component(Offcanvas)]
67pub fn offcanvas(props: &OffcanvasProps) -> Html {
68    let mut classes_vec = vec!["offcanvas".to_string()];
69    classes_vec.push(format!("offcanvas-{}", props.placement.as_str()));
70    if let Some(class) = &props.class {
71        classes_vec.push(class.to_string());
72    }
73    let classes = classes!(classes_vec);
74    let node_ref = props.node_ref.clone();
75    {
76        let show = props.show;
77        let backdrop = props.backdrop;
78        let backdrop_close = props.backdrop_close;
79        let keyboard = props.keyboard;
80        let scroll = props.scroll;
81        let on_show = props.on_show.clone();
82        let on_hide = props.on_hide.clone();
83        let on_shown = props.on_shown.clone();
84        let on_hidden = props.on_hidden.clone();
85        let node_ref = node_ref.clone();
86        use_effect_with(
87            (show, backdrop, backdrop_close, keyboard, scroll),
88            move |(show, backdrop, backdrop_close, keyboard, scroll)| {
89                if let Some(element) = node_ref.cast::<Element>() {
90                    let options = js_sys::Object::new();
91                    if *backdrop {
92                        if *backdrop_close {
93                            js_sys::Reflect::set(
94                                    &options,
95                                    &"backdrop".into(),
96                                    &true.into(),
97                                )
98                                .unwrap();
99                        } else {
100                            js_sys::Reflect::set(
101                                    &options,
102                                    &"backdrop".into(),
103                                    &"static".into(),
104                                )
105                                .unwrap();
106                        }
107                    } else {
108                        js_sys::Reflect::set(&options, &"backdrop".into(), &false.into())
109                            .unwrap();
110                    }
111                    js_sys::Reflect::set(
112                            &options,
113                            &"keyboard".into(),
114                            &(*keyboard).into(),
115                        )
116                        .unwrap();
117                    js_sys::Reflect::set(&options, &"scroll".into(), &(*scroll).into())
118                        .unwrap();
119                    let bs_offcanvas = BsOffcanvas::new(
120                        &element,
121                        Some(&JsValue::from(options)),
122                    );
123                    if *show {
124                        bs_offcanvas.show();
125                    } else {
126                        bs_offcanvas.hide();
127                    }
128                }
129                || ()
130            },
131        );
132    }
133    html! {
134        < div class = { classes } tabindex = "-1" ref = { node_ref } > { for props
135        .children.iter() } </ div >
136    }
137}
138/// Props for the OffcanvasHeader component
139#[derive(Properties, PartialEq)]
140pub struct OffcanvasHeaderProps {
141    /// Header children
142    #[prop_or_default]
143    pub children: Children,
144    /// Additional CSS classes
145    #[prop_or_default]
146    pub class: Option<AttrValue>,
147}
148/// Offcanvas header component
149#[function_component(OffcanvasHeader)]
150pub fn offcanvas_header(props: &OffcanvasHeaderProps) -> Html {
151    let mut classes_vec = vec!["offcanvas-header".to_string()];
152    if let Some(class) = &props.class {
153        classes_vec.push(class.to_string());
154    }
155    let classes = classes!(classes_vec);
156    html! {
157        < div class = { classes } > { for props.children.iter() } </ div >
158    }
159}
160/// Props for the OffcanvasTitle component
161#[derive(Properties, PartialEq)]
162pub struct OffcanvasTitleProps {
163    /// Title text
164    #[prop_or_default]
165    pub children: Children,
166    /// Additional CSS classes
167    #[prop_or_default]
168    pub class: Option<AttrValue>,
169}
170/// Offcanvas title component
171#[function_component(OffcanvasTitle)]
172pub fn offcanvas_title(props: &OffcanvasTitleProps) -> Html {
173    let mut classes_vec = vec!["offcanvas-title".to_string()];
174    if let Some(class) = &props.class {
175        classes_vec.push(class.to_string());
176    }
177    let classes = classes!(classes_vec);
178    html! {
179        < h5 class = { classes } > { for props.children.iter() } </ h5 >
180    }
181}
182/// Props for the OffcanvasBody component
183#[derive(Properties, PartialEq)]
184pub struct OffcanvasBodyProps {
185    /// Body children
186    #[prop_or_default]
187    pub children: Children,
188    /// Additional CSS classes
189    #[prop_or_default]
190    pub class: Option<AttrValue>,
191}
192/// Offcanvas body component
193#[function_component(OffcanvasBody)]
194pub fn offcanvas_body(props: &OffcanvasBodyProps) -> Html {
195    let mut classes_vec = vec!["offcanvas-body".to_string()];
196    if let Some(class) = &props.class {
197        classes_vec.push(class.to_string());
198    }
199    let classes = classes!(classes_vec);
200    html! {
201        < div class = { classes } > { for props.children.iter() } </ div >
202    }
203}