yew_bootstrap/component/
button.rs

1use crate::util::Color;
2use yew::prelude::*;
3
4#[derive(Clone, PartialEq, Eq)]
5pub enum ButtonSize {
6    Large,
7    Normal,
8    Small,
9}
10
11impl Default for ButtonSize {
12    fn default() -> Self {
13        ButtonSize::Normal
14    }
15}
16
17/// # Button component
18/// Button with various properties, including support for opening or closing a modal
19/// dialog [crate::component::Modal].
20///
21/// Buttons can be grouped in a [crate::component::ButtonGroup].
22///
23/// See [ButtonProps] for a listing of properties.
24///
25/// ## Example
26/// Example of a simple button:
27///
28/// ```rust
29/// use yew::prelude::*;
30/// use yew_bootstrap::component::Button;
31/// use yew_bootstrap::util::Color;
32/// fn test() -> Html {
33///     html!{
34///         <Button style={Color::Primary} text={ "Button text" }/>
35///     }
36/// }
37/// ```
38///
39/// A button can be linked to a [crate::component::Modal] dialog or
40/// close this modal.
41///
42/// ```rust
43/// use yew::prelude::*;
44/// use yew_bootstrap::component::Button;
45/// use yew_bootstrap::component::Modal;
46/// use yew_bootstrap::util::Color;
47/// fn test() -> Html {
48///     html ! {
49///         <>
50///             <Modal id="ExampleModal">
51///                <Button modal_dismiss={true}>{ "Close the modal" }</Button>
52///             </Modal>
53///             <Button style={Color::Primary} modal_target={ "ExampleModal" }>
54///                 { "Open Modal" }
55///             </Button>
56///         </>
57///     }
58/// }
59/// ```
60///
61/// A button may also link to a web page.
62///
63/// ```rust
64/// use yew::prelude::*;
65/// use yew_bootstrap::component::Button;
66/// use yew_bootstrap::util::Color;
67/// fn test() -> Html {
68///     html!{
69///         <Button style={Color::Primary} text={ "Button text" } url={ "https://getbootstrap.com/docs/5.3/components/buttons/#button-tags" } target={"_blank"} />
70///     }
71/// }
72/// ```
73pub struct Button {}
74
75/// # Properties for [Button]
76#[derive(Properties, Clone, PartialEq)]
77pub struct ButtonProps {
78    /// CSS class
79    #[prop_or_default]
80    pub class: String,
81
82    /// Optional children
83    #[prop_or_default]
84    pub children: Children,
85
86    /// Treat button as block that spans the full width of the parent
87    #[prop_or_default]
88    pub block: bool,
89
90    /// Status of the button. Disabled buttons cannot be clicked.
91    #[prop_or_default]
92    pub disabled: bool,
93
94    /// Name of the component
95    #[prop_or_default]
96    pub name: String,
97
98    /// Event called when the button is clicked
99    #[prop_or_default]
100    pub onclick: Callback<MouseEvent>,
101
102    /// Show button as outlined instead of filled
103    #[prop_or_default]
104    pub outline: bool,
105
106    /// Size of the button
107    #[prop_or_default]
108    pub size: ButtonSize,
109
110    /// Color of the button, default [Color::Primary]
111    #[prop_or(Color::Primary)]
112    pub style: Color,
113
114    /// Text displayed in the button
115    #[prop_or_default]
116    pub text: String,
117
118    /// if provided, we will set data-bs-toggle and data-bs-target for modal opening
119    #[prop_or_default]
120    pub modal_target: Option<String>,
121
122    /// true if this button dismisses the modal that contains it
123    #[prop_or_default]
124    pub modal_dismiss: bool,
125
126    /// URL to direct to when the button is clicked. This turns the button into
127    /// an `<a>` element.
128    ///
129    /// This property is ignored if the button is `disabled` to
130    /// [avoid link functionality caveats][0], which may result in
131    /// [slightly different rendering on some browsers / platforms][1].
132    ///
133    /// [0]: https://getbootstrap.com/docs/5.3/components/buttons/#link-functionality-caveat
134    /// [1]: https://getbootstrap.com/docs/5.3/components/buttons/#button-tags
135    #[prop_or_default]
136    pub url: Option<AttrValue>,
137
138    /// Target frame or window ID for the link. Only used if `url` is set and
139    /// the button is not `disabled`.
140    #[prop_or_default]
141    pub target: Option<AttrValue>,
142
143    /// Reference to the [NodeRef] of the button's underlying `<button>` or
144    /// `<a>` element.
145    ///
146    /// Used by components which add custom event handlers directly to the DOM.
147    /// 
148    /// See [*Node Refs* in the Yew documentation][0] for more information.
149    /// 
150    /// [0]: https://yew.rs/docs/concepts/function-components/node-refs
151    #[prop_or_default]
152    pub node_ref: NodeRef,
153
154    /// Optional HTML element ID for the underlying `<button>` or `<a>` element.
155    #[prop_or_default]
156    pub id: Option<AttrValue>,
157}
158
159impl Component for Button {
160    type Message = ();
161    type Properties = ButtonProps;
162
163    fn create(_ctx: &Context<Self>) -> Self {
164        Self {}
165    }
166
167    fn view(&self, ctx: &Context<Self>) -> Html {
168        let props = ctx.props();
169        let mut classes = Classes::new();
170        classes.push("btn");
171        if props.outline {
172            classes.push(format!("btn-outline-{}", props.style));
173        } else {
174            classes.push(format!("btn-{}", props.style));
175        }
176        match props.size {
177            ButtonSize::Large => classes.push("btn-lg"),
178            ButtonSize::Small => classes.push("btn-sm"),
179            _ => (),
180        }
181        if props.block {
182            classes.push("btn-block");
183        }
184        classes.push(props.class.clone());
185
186        let modal_dismiss = match props.modal_dismiss {
187            true => "modal",
188            false => "",
189        };
190
191        if let Some(target) = &props.modal_target {
192            html! {
193                <button
194                    class={classes}
195                    disabled={props.disabled}
196                    name={props.name.clone()}
197                    onclick={props.onclick.clone()}
198                    data-bs-toggle="modal"
199                    data-bs-target={format!("#{}",target.clone())}
200                    ref={props.node_ref.clone()}
201                    id={props.id.clone()}
202                >
203                    { &props.text }
204                    { for props.children.iter() }
205                </button>
206            }
207        } else if let Some(url) = props.url.as_ref().filter(|_| !props.disabled) {
208            html! {
209                <a
210                    class={classes}
211                    disabled={props.disabled}
212                    name={props.name.clone()}
213                    onclick={props.onclick.clone()}
214                    data-bs-dismiss={modal_dismiss}
215                    href={url.clone()}
216                    target={props.target.clone()}
217                    ref={props.node_ref.clone()}
218                    id={props.id.clone()}
219                >
220                    { &props.text }
221                    { for props.children.iter() }
222                </a>
223            }
224        } else {
225            html! {
226                <button
227                    class={classes}
228                    disabled={props.disabled}
229                    name={props.name.clone()}
230                    onclick={props.onclick.clone()}
231                    data-bs-dismiss={modal_dismiss}
232                    ref={props.node_ref.clone()}
233                    id={props.id.clone()}
234                >
235                    { &props.text }
236                    { for props.children.iter() }
237                </button>
238            }
239        }
240    }
241}