yew_bs/components/
popovers.rs

1use yew::prelude::*;
2use web_sys::Element;
3use wasm_bindgen::JsValue;
4use crate::interop::BsPopover;
5#[derive(Clone, Copy, PartialEq, Debug)]
6pub enum PopoverPlacement {
7    Auto,
8    Top,
9    Bottom,
10    Left,
11    Right,
12}
13impl PopoverPlacement {
14    pub fn as_str(&self) -> &'static str {
15        match self {
16            PopoverPlacement::Auto => "auto",
17            PopoverPlacement::Top => "top",
18            PopoverPlacement::Bottom => "bottom",
19            PopoverPlacement::Left => "left",
20            PopoverPlacement::Right => "right",
21        }
22    }
23}
24/// Popover trigger options
25#[derive(Clone, Copy, PartialEq, Debug)]
26pub enum PopoverTrigger {
27    Click,
28    Hover,
29    Focus,
30    Manual,
31}
32impl PopoverTrigger {
33    pub fn as_str(&self) -> &'static str {
34        match self {
35            PopoverTrigger::Click => "click",
36            PopoverTrigger::Hover => "hover",
37            PopoverTrigger::Focus => "focus",
38            PopoverTrigger::Manual => "manual",
39        }
40    }
41}
42#[derive(Properties, PartialEq)]
43pub struct PopoverProps {
44    #[prop_or_default]
45    pub children: Children,
46    #[prop_or_default]
47    pub title: Option<AttrValue>,
48    #[prop_or_default]
49    pub content: Option<AttrValue>,
50    #[prop_or(PopoverPlacement::Top)]
51    pub placement: PopoverPlacement,
52    #[prop_or(PopoverTrigger::Click)]
53    pub trigger: PopoverTrigger,
54    #[prop_or_default]
55    pub show: bool,
56    #[prop_or_default]
57    pub allow_html: bool,
58    #[prop_or_default]
59    pub template: Option<AttrValue>,
60    #[prop_or(true)]
61    pub animation: bool,
62    #[prop_or_default]
63    pub delay: Option<u32>,
64    #[prop_or_default]
65    pub class: Option<AttrValue>,
66    #[prop_or_default]
67    pub node_ref: NodeRef,
68}
69#[function_component(Popover)]
70pub fn popover(props: &PopoverProps) -> Html {
71    let node_ref = props.node_ref.clone();
72    {
73        let node_ref = node_ref.clone();
74        let title = props.title.clone();
75        let content = props.content.clone();
76        let placement = props.placement;
77        let trigger = props.trigger;
78        let show = props.show;
79        let allow_html = props.allow_html;
80        let template = props.template.clone();
81        let animation = props.animation;
82        let delay = props.delay;
83        use_effect_with(
84            (),
85            move |_| {
86                if let Some(element) = node_ref.cast::<Element>() {
87                    let options = js_sys::Object::new();
88                    if let Some(title) = &title {
89                        js_sys::Reflect::set(
90                                &options,
91                                &"title".into(),
92                                &title.as_str().into(),
93                            )
94                            .unwrap();
95                    }
96                    if let Some(content) = &content {
97                        js_sys::Reflect::set(
98                                &options,
99                                &"content".into(),
100                                &content.as_str().into(),
101                            )
102                            .unwrap();
103                    }
104                    js_sys::Reflect::set(
105                            &options,
106                            &"placement".into(),
107                            &placement.as_str().into(),
108                        )
109                        .unwrap();
110                    js_sys::Reflect::set(
111                            &options,
112                            &"trigger".into(),
113                            &trigger.as_str().into(),
114                        )
115                        .unwrap();
116                    js_sys::Reflect::set(&options, &"html".into(), &allow_html.into())
117                        .unwrap();
118                    js_sys::Reflect::set(
119                            &options,
120                            &"animation".into(),
121                            &animation.into(),
122                        )
123                        .unwrap();
124                    if let Some(delay) = delay {
125                        js_sys::Reflect::set(&options, &"delay".into(), &delay.into())
126                            .unwrap();
127                    }
128                    if let Some(template) = &template {
129                        js_sys::Reflect::set(
130                                &options,
131                                &"template".into(),
132                                &template.as_str().into(),
133                            )
134                            .unwrap();
135                    }
136                    let bs_popover = BsPopover::new(
137                        &element,
138                        Some(&JsValue::from(options)),
139                    );
140                    if show {
141                        bs_popover.show();
142                    }
143                }
144                || ()
145            },
146        );
147    }
148    html! {
149        < span ref = { node_ref } class = { props.class.clone() } > { for props.children
150        .iter() } </ span >
151    }
152}
153#[derive(Properties, PartialEq)]
154pub struct PopoverHeaderProps {
155    #[prop_or_default]
156    pub children: Children,
157    #[prop_or_default]
158    pub class: Option<AttrValue>,
159}
160#[function_component(PopoverHeader)]
161pub fn popover_header(props: &PopoverHeaderProps) -> Html {
162    let mut classes = "popover-header".to_string();
163    if let Some(class) = &props.class {
164        classes.push(' ');
165        classes.push_str(class.as_str());
166    }
167    html! {
168        < div class = { classes } > { for props.children.iter() } </ div >
169    }
170}
171#[derive(Properties, PartialEq)]
172pub struct PopoverBodyProps {
173    #[prop_or_default]
174    pub children: Children,
175    #[prop_or_default]
176    pub class: Option<AttrValue>,
177}
178#[function_component(PopoverBody)]
179pub fn popover_body(props: &PopoverBodyProps) -> Html {
180    let mut classes = "popover-body".to_string();
181    if let Some(class) = &props.class {
182        classes.push(' ');
183        classes.push_str(class.as_str());
184    }
185    html! {
186        < div class = { classes } > { for props.children.iter() } </ div >
187    }
188}