yew_and_bulma/elements/
button.rs

1use yew::{function_component, html, Children, Classes, Html, Properties};
2use yew_and_bulma_macros::base_component_properties;
3
4use crate::{
5    helpers::color::Color,
6    utils::size::Size,
7    utils::{
8        class::ClassBuilder,
9        constants::{ARE_PREFIX, IS_PREFIX},
10    },
11};
12
13/// Defines the possible alignment of buttons from a [buttons element][bd].
14///
15/// Defines the possible alignment of buttons found inside a
16/// [Bulma buttons element][bd].
17///
18/// # Examples
19///
20/// ```rust
21/// use yew::prelude::*;
22/// use yew_and_bulma::elements::button::{Button, Buttons, Align};
23///
24/// #[function_component(App)]
25/// fn app() -> Html {
26///     html! {
27///         <Buttons align={Align::Center}>
28///             <Button>{"Button"}</Button>
29///             <Button>{"Button"}</Button>
30///             <Button>{"Button"}</Button>
31///         </Buttons>
32///     }
33/// }
34/// ```
35///
36/// [bd]: https://bulma.io/documentation/elements/button/#list-of-buttons
37#[derive(PartialEq)]
38pub enum Align {
39    // TODO: use #[default] when updating the MSRV
40    Left,
41    Center,
42    Right,
43}
44
45/// Defines the properties of the [Bulma buttons element][bd].
46///
47/// Defines the properties of the buttons element, based on the specification
48/// found in the [Bulma buttons element documentation][bd].
49///
50/// # Examples
51///
52/// ```rust
53/// use yew::prelude::*;
54/// use yew_and_bulma::elements::button::{Button, Buttons};
55///
56/// #[function_component(App)]
57/// fn app() -> Html {
58///     html! {
59///         <Buttons>
60///             <Button>{"Button"}</Button>
61///         </Buttons>
62///     }
63/// }
64/// ```
65///
66/// [bd]: https://bulma.io/documentation/elements/button/#list-of-buttons
67#[base_component_properties]
68#[derive(Properties, PartialEq)]
69pub struct ButtonsProperties {
70    /// Sets the size of the buttons found inside the [buttons element][bd].
71    ///
72    /// Sets the size of the buttons that will be found inside the
73    /// [Bulma buttons element][bd] which will receive these properties.
74    ///
75    /// # Examples
76    ///
77    /// ```rust
78    /// use yew::prelude::*;
79    /// use yew_and_bulma::{
80    ///     elements::button::{Button, Buttons},
81    ///     utils::size::Size,
82    /// };
83    ///
84    /// #[function_component(App)]
85    /// fn app() -> Html {
86    ///     html! {
87    ///         <Buttons size={Size::Large}>
88    ///             <Button>{"Button"}</Button>
89    ///             <Button>{"Button"}</Button>
90    ///             <Button>{"Button"}</Button>
91    ///         </Buttons>
92    ///     }
93    /// }
94    /// ```
95    ///
96    /// [bd]: https://bulma.io/documentation/elements/button/#list-of-buttons
97    #[prop_or_default]
98    pub size: Option<Size>,
99    /// Whether to attach the buttons found inside the [buttons element][bd].
100    ///
101    /// Whether or not to attach the buttons that will be found inside the
102    /// [Bulma buttons element][bd] which will receive these properties.
103    ///
104    /// # Examples
105    ///
106    /// ```rust
107    /// use yew::prelude::*;
108    /// use yew_and_bulma::{
109    ///     elements::button::{Button, Buttons},
110    ///     utils::size::Size,
111    /// };
112    ///
113    /// #[function_component(App)]
114    /// fn app() -> Html {
115    ///     html! {
116    ///         <Buttons addons=true>
117    ///             <Button>{"Button"}</Button>
118    ///             <Button>{"Button"}</Button>
119    ///             <Button>{"Button"}</Button>
120    ///         </Buttons>
121    ///     }
122    /// }
123    /// ```
124    ///
125    /// [bd]: https://bulma.io/documentation/elements/button/#list-of-buttons
126    #[prop_or_default]
127    pub addons: bool,
128    /// Sets the alignment of buttons from a [buttons element][bd].
129    ///
130    /// Sets the alignment of buttons found inside a
131    /// [Bulma buttons element][bd] which will receive these properties.
132    ///
133    /// # Examples
134    ///
135    /// ```rust
136    /// use yew::prelude::*;
137    /// use yew_and_bulma::elements::button::{Button, Buttons, Align};
138    ///
139    /// #[function_component(App)]
140    /// fn app() -> Html {
141    ///     html! {
142    ///         <Buttons align={Align::Center}>
143    ///             <Button>{"Button"}</Button>
144    ///             <Button>{"Button"}</Button>
145    ///             <Button>{"Button"}</Button>
146    ///         </Buttons>
147    ///     }
148    /// }
149    /// ```
150    ///
151    /// [bd]: https://bulma.io/documentation/elements/button/#list-of-buttons
152    #[prop_or(Align::Left)]
153    pub align: Align,
154    /// The list of elements found inside the [buttons element][bd].
155    ///
156    /// Defines the elements that will be found inside the
157    /// [Bulma buttons element][bd] which will receive these properties.
158    ///
159    /// [bd]: https://bulma.io/documentation/elements/button/#list-of-buttons
160    pub children: Children,
161}
162
163impl From<&Align> for String {
164    fn from(value: &Align) -> Self {
165        match value {
166            Align::Left => "".to_owned(),
167            Align::Center => format!("{IS_PREFIX}-centered"),
168            Align::Right => format!("{IS_PREFIX}-right"),
169        }
170    }
171}
172
173/// Yew implementation of the [Bulma buttons element][bd].
174///
175/// Yew implementation of the buttons element, based on the specification found
176/// in the [Bulma buttons element documentation][bd].
177///
178/// # Examples
179///
180/// ```rust
181/// use yew::prelude::*;
182/// use yew_and_bulma::elements::button::{Button, Buttons};
183///
184/// #[function_component(App)]
185/// fn app() -> Html {
186///     html! {
187///         <Buttons>
188///             <Button>{"A button"}</Button>
189///             <Button>{"Another button"}</Button>
190///         </Buttons>
191///     }
192/// }
193/// ```
194///
195/// [bd]: https://bulma.io/documentation/elements/button/
196#[function_component(Buttons)]
197pub fn buttons(props: &ButtonsProperties) -> Html {
198    let size = props
199        .size
200        .as_ref()
201        .map(|size| {
202            if Size::Normal == *size {
203                "".to_owned()
204            } else {
205                format!("{ARE_PREFIX}-{size}")
206            }
207        })
208        .unwrap_or("".to_owned());
209    let addons = if props.addons { "has-addons" } else { "" }.to_owned();
210    let class = ClassBuilder::default()
211        .with_custom_class("buttons")
212        .with_custom_class(&size)
213        .with_custom_class(&addons)
214        .with_custom_class(&String::from(&props.align))
215        .with_custom_class(
216            &props
217                .class
218                .as_ref()
219                .map(|c| c.to_string())
220                .unwrap_or("".to_owned()),
221        )
222        .build();
223
224    html! {
225        <div id={props.id.clone()} {class}
226            onclick={props.onclick.clone()} onwheel={props.onwheel.clone()} onscroll={props.onscroll.clone()}
227            onmousedown={props.onmousedown.clone()} onmousemove={props.onmousemove.clone()} onmouseout={props.onmouseout.clone()} onmouseover={props.onmouseover.clone()} onmouseup={props.onmouseup.clone()}
228            ondrag={props.ondrag.clone()} ondragend={props.ondragend.clone()} ondragenter={props.ondragenter.clone()} ondragleave={props.ondragleave.clone()} ondragover={props.ondragover.clone()} ondragstart={props.ondragstart.clone()} ondrop={props.ondrop.clone()}
229            oncopy={props.oncopy.clone()} oncut={props.oncut.clone()} onpaste={props.onpaste.clone()}
230            onkeydown={props.onkeydown.clone()} onkeypress={props.onkeypress.clone()} onkeyup={props.onkeyup.clone()}
231            onblur={props.onblur.clone()} onchange={props.onchange.clone()} oncontextmenu={props.oncontextmenu.clone()} onfocus={props.onfocus.clone()} oninput={props.oninput.clone()} oninvalid={props.oninvalid.clone()} onreset={props.onreset.clone()} onselect={props.onselect.clone()} onsubmit={props.onsubmit.clone()}
232            onabort={props.onabort.clone()} oncanplay={props.oncanplay.clone()} oncanplaythrough={props.oncanplaythrough.clone()} oncuechange={props.oncuechange.clone()}
233            ondurationchange={props.ondurationchange.clone()} onemptied={props.onemptied.clone()} onended={props.onended.clone()} onerror={props.onerror.clone()}
234            onloadeddata={props.onloadeddata.clone()} onloadedmetadata={props.onloadedmetadata.clone()} onloadstart={props.onloadstart.clone()} onpause={props.onpause.clone()}
235            onplay={props.onplay.clone()} onplaying={props.onplaying.clone()} onprogress={props.onprogress.clone()} onratechange={props.onratechange.clone()}
236            onseeked={props.onseeked.clone()} onseeking={props.onseeking.clone()} onstalled={props.onstalled.clone()} onsuspend={props.onsuspend.clone()}
237            ontimeupdate={props.ontimeupdate.clone()} onvolumechange={props.onvolumechange.clone()} onwaiting={props.onwaiting.clone()}>
238            { for props.children.iter() }
239        </div>
240    }
241}
242
243/// Defines the possible states of a [button element][bd].
244///
245/// Defines the possible states of a [Bulma button element][bd].
246///
247/// # Examples
248///
249/// ```rust
250/// use yew::prelude::*;
251/// use yew_and_bulma::elements::button::{Button, State};
252///
253/// #[function_component(App)]
254/// fn app() -> Html {
255///     html! {
256///         <Button state={State::Loading}>{"Button"}</Button>
257///     }
258/// }
259/// ```
260///
261/// [bd]: https://bulma.io/documentation/elements/button/#states
262#[derive(PartialEq)]
263pub enum State {
264    Normal,
265    Hover,
266    Focus,
267    Active,
268    Loading,
269    Static,
270}
271
272impl From<&State> for String {
273    fn from(value: &State) -> Self {
274        let state = match value {
275            State::Normal => "normal",
276            State::Hover => "hover",
277            State::Focus => "focus",
278            State::Active => "active",
279            State::Loading => "loading",
280            State::Static => "static",
281        };
282
283        format!("{IS_PREFIX}-{state}")
284    }
285}
286
287/// Defines the possible style of a [button element][bd].
288///
289/// Defines the possible style of a [Bulma button element][bd].
290///
291/// # Examples
292///
293/// ```rust
294/// use yew::prelude::*;
295/// use yew_and_bulma::elements::button::{Button, Style};
296///
297/// #[function_component(App)]
298/// fn app() -> Html {
299///     html! {
300///         <Button style={Style::Outlined}>{"Button"}</Button>
301///     }
302/// }
303/// ```
304///
305/// [bd]: https://bulma.io/documentation/elements/button/#style
306#[derive(PartialEq)]
307pub enum Style {
308    Outlined,
309    Inverted,
310    InvertedOutlined,
311    Rounded,
312}
313
314impl From<&Style> for String {
315    fn from(value: &Style) -> Self {
316        match value {
317            Style::Outlined => format!("{IS_PREFIX}-outlined"),
318            Style::Inverted => format!("{IS_PREFIX}-inverted"),
319            Style::InvertedOutlined => format!("{IS_PREFIX}-inverted {IS_PREFIX}-outlined"),
320            Style::Rounded => format!("{IS_PREFIX}-rounded"),
321        }
322    }
323}
324
325/// Defines the properties of the [Bulma button element][bd].
326///
327/// Defines the properties of the button element, based on the specification
328/// found in the [Bulma button element documentation][bd].
329///
330/// # Examples
331///
332/// ```rust
333/// use yew::prelude::*;
334/// use yew_and_bulma::elements::button::Button;
335///
336/// #[function_component(App)]
337/// fn app() -> Html {
338///     html! {
339///         <Button>{"Button"}</Button>
340///     }
341/// }
342/// ```
343///
344/// [bd]: https://bulma.io/documentation/elements/button/
345#[base_component_properties]
346#[derive(Properties, PartialEq)]
347pub struct ButtonProperties {
348    /// Sets the color of the [Bulma button element][bd].
349    ///
350    /// Sets the color of the [Bulma button element][bd] which will receive
351    /// these properties.
352    ///
353    /// # Examples
354    ///
355    /// ```rust
356    /// use yew::prelude::*;
357    /// use yew_and_bulma::{
358    ///     elements::button::Button,
359    ///     helpers::color::Color,
360    /// };
361    ///
362    /// #[function_component(App)]
363    /// fn app() -> Html {
364    ///     html! {
365    ///         <Button color={Color::Primary}>{"Button"}</Button>
366    ///     }
367    /// }
368    /// ```
369    ///
370    /// [bd]: https://bulma.io/documentation/elements/button/#colors
371    #[prop_or_default]
372    pub color: Option<Color>,
373    /// Whether or not the color of the [button element][bd] should be light.
374    ///
375    /// Whether or not the color of the [Bulma button element][bd], which will
376    /// receive these properties, should be of the light variant.
377    ///
378    /// # Examples
379    ///
380    /// ```rust
381    /// use yew::prelude::*;
382    /// use yew_and_bulma::{
383    ///     elements::button::Button,
384    ///     helpers::color::Color,
385    /// };
386    ///
387    /// #[function_component(App)]
388    /// fn app() -> Html {
389    ///     html! {
390    ///         <Button light=true color={Color::Primary}>{"Button"}</Button>
391    ///     }
392    /// }
393    /// ```
394    ///
395    /// [bd]: https://bulma.io/documentation/elements/button/#colors
396    #[prop_or_default]
397    pub light: Option<bool>,
398    /// Sets the size of the [Bulma button element][bd].
399    ///
400    /// Sets the size of the [Bulma button element][bd] which will receive
401    /// these properties.
402    ///
403    /// # Examples
404    ///
405    /// ```rust
406    /// use yew::prelude::*;
407    /// use yew_and_bulma::{
408    ///     elements::button::Button,
409    ///     utils::size::Size,
410    /// };
411    ///
412    /// #[function_component(App)]
413    /// fn app() -> Html {
414    ///     html! {
415    ///         <Button size={Size::Large}>{"Button"}</Button>
416    ///     }
417    /// }
418    /// ```
419    ///
420    /// [bd]: https://bulma.io/documentation/elements/button/#sizes
421    #[prop_or_default]
422    pub size: Option<Size>,
423    /// Whether the size of the [button element][bd] should be responsive.
424    ///
425    /// Whether or not the size of the [Bulma button element][bd], which will
426    /// receive these properties, will be responsive.
427    ///
428    /// # Examples
429    ///
430    /// ```rust
431    /// use yew::prelude::*;
432    /// use yew_and_bulma::{
433    ///     elements::button::Button,
434    ///     utils::size::Size,
435    /// };
436    ///
437    /// #[function_component(App)]
438    /// fn app() -> Html {
439    ///     html! {
440    ///         <Button responsive=true size={Size::Large}>{"Button"}</Button>
441    ///     }
442    /// }
443    /// ```
444    ///
445    /// [bd]: https://bulma.io/documentation/elements/button/#responsive-sizes
446    #[prop_or_default]
447    pub responsive: bool,
448    /// Whether the [button element][bd] should have the width of its parent.
449    ///
450    /// Whether or not the [Bulma button element][bd], which will receive these
451    /// properties, will have the same width as its parent.
452    ///
453    /// # Examples
454    ///
455    /// ```rust
456    /// use yew::prelude::*;
457    /// use yew_and_bulma::elements::button::Button;
458    ///
459    /// #[function_component(App)]
460    /// fn app() -> Html {
461    ///     html! {
462    ///         <Button fullwidth=true>{"Button"}</Button>
463    ///     }
464    /// }
465    /// ```
466    ///
467    /// [bd]: https://bulma.io/documentation/elements/button/#displays
468    #[prop_or_default]
469    pub fullwidth: bool,
470    /// Sets the style of the [Bulma button element][bd].
471    ///
472    /// Sets the style of the [Bulma button element][bd] which will receive
473    /// these properties.
474    ///
475    /// # Examples
476    ///
477    /// ```rust
478    /// use yew::prelude::*;
479    /// use yew_and_bulma::elements::button::{Button, Style};
480    ///
481    /// #[function_component(App)]
482    /// fn app() -> Html {
483    ///     html! {
484    ///         <Button style={Style::Outlined}>{"Button"}</Button>
485    ///     }
486    /// }
487    /// ```
488    ///
489    /// [bd]: https://bulma.io/documentation/elements/button/#styles
490    #[prop_or_default]
491    pub style: Option<Style>,
492    /// Sets the state of the [Bulma button element][bd].
493    ///
494    /// Sets the state of the [Bulma button element][bd] which will receive
495    /// these properties.
496    ///
497    /// # Examples
498    ///
499    /// ```rust
500    /// use yew::prelude::*;
501    /// use yew_and_bulma::elements::button::{Button, State};
502    ///
503    /// #[function_component(App)]
504    /// fn app() -> Html {
505    ///     html! {
506    ///         <Button state={State::Loading}>
507    ///             {"This label will be a spinner"}
508    ///         </Button>
509    ///     }
510    /// }
511    /// ```
512    ///
513    /// [bd]: https://bulma.io/documentation/elements/button/#states
514    #[prop_or_default]
515    pub state: Option<State>,
516    /// Whether or not the [Bulma button element][bd] should be disabled.
517    ///
518    /// Whether or not the [Bulma button element][bd], which will receive these
519    /// properties, will be disabled. This means it will have the *HTML
520    /// attribute* `disabled` set.
521    ///
522    /// # Examples
523    ///
524    /// ```rust
525    /// use yew::prelude::*;
526    /// use yew_and_bulma::elements::button::Button;
527    ///
528    /// #[function_component(App)]
529    /// fn app() -> Html {
530    ///     html! {
531    ///         <Button disabled=true>{"Button"}</Button>
532    ///     }
533    /// }
534    /// ```
535    ///
536    /// [bd]: https://bulma.io/documentation/elements/button/#displays
537    #[prop_or_default]
538    pub disabled: bool,
539    /// The list of elements found inside the [button element][bd].
540    ///
541    /// Defines the elements that will be found inside the
542    /// [Bulma button element][bd] which will receive these properties.
543    ///
544    /// [bd]: https://bulma.io/documentation/elements/button/
545    pub children: Children,
546}
547
548impl From<&ButtonProperties> for Classes {
549    fn from(value: &ButtonProperties) -> Self {
550        let size = value
551            .size
552            .as_ref()
553            .map(|size| {
554                if Size::Normal == *size {
555                    "".to_owned()
556                } else {
557                    format!("{IS_PREFIX}-{size}")
558                }
559            })
560            .unwrap_or("".to_owned());
561        let style = value
562            .style
563            .as_ref()
564            .map(String::from)
565            .unwrap_or("".to_string());
566        let fullwidth = if value.fullwidth {
567            format!("{IS_PREFIX}-fullwidth")
568        } else {
569            "".to_owned()
570        };
571        let responsive = if value.responsive {
572            format!("{IS_PREFIX}-responsive")
573        } else {
574            "".to_string()
575        };
576        let state = value
577            .state
578            .as_ref()
579            .map(String::from)
580            .unwrap_or("".to_owned());
581
582        ClassBuilder::default()
583            .with_custom_class("button")
584            .with_color(value.color)
585            .is_light(value.light)
586            .with_custom_class(&size)
587            .with_custom_class(&responsive)
588            .with_custom_class(&fullwidth)
589            .with_custom_class(&style)
590            .with_custom_class(&state)
591            .with_custom_class(
592                &value
593                    .class
594                    .as_ref()
595                    .map(|c| c.to_string())
596                    .unwrap_or("".to_owned()),
597            )
598            .build()
599    }
600}
601
602/// Yew implementation of the [Bulma button element][bd].
603///
604/// Yew implementation of the button element, based on the specification found
605/// in the [Bulma button element documentation][bd].
606///
607/// # Examples
608///
609/// ```rust
610/// use yew::prelude::*;
611/// use yew_and_bulma::elements::button::Button;
612///
613/// #[function_component(App)]
614/// fn app() -> Html {
615///     html! {
616///         <Button>{"A button"}</Button>
617///     }
618/// }
619/// ```
620///
621/// [bd]: https://bulma.io/documentation/elements/button/
622#[function_component(Button)]
623pub fn button(props: &ButtonProperties) -> Html {
624    let class: Classes = props.into();
625
626    html! {
627        <button id={props.id.clone()} {class} disabled={props.disabled}
628            onclick={props.onclick.clone()} onwheel={props.onwheel.clone()} onscroll={props.onscroll.clone()}
629            onmousedown={props.onmousedown.clone()} onmousemove={props.onmousemove.clone()} onmouseout={props.onmouseout.clone()} onmouseover={props.onmouseover.clone()} onmouseup={props.onmouseup.clone()}
630            ondrag={props.ondrag.clone()} ondragend={props.ondragend.clone()} ondragenter={props.ondragenter.clone()} ondragleave={props.ondragleave.clone()} ondragover={props.ondragover.clone()} ondragstart={props.ondragstart.clone()} ondrop={props.ondrop.clone()}
631            oncopy={props.oncopy.clone()} oncut={props.oncut.clone()} onpaste={props.onpaste.clone()}
632            onkeydown={props.onkeydown.clone()} onkeypress={props.onkeypress.clone()} onkeyup={props.onkeyup.clone()}
633            onblur={props.onblur.clone()} onchange={props.onchange.clone()} oncontextmenu={props.oncontextmenu.clone()} onfocus={props.onfocus.clone()} oninput={props.oninput.clone()} oninvalid={props.oninvalid.clone()} onreset={props.onreset.clone()} onselect={props.onselect.clone()} onsubmit={props.onsubmit.clone()}
634            onabort={props.onabort.clone()} oncanplay={props.oncanplay.clone()} oncanplaythrough={props.oncanplaythrough.clone()} oncuechange={props.oncuechange.clone()}
635            ondurationchange={props.ondurationchange.clone()} onemptied={props.onemptied.clone()} onended={props.onended.clone()} onerror={props.onerror.clone()}
636            onloadeddata={props.onloadeddata.clone()} onloadedmetadata={props.onloadedmetadata.clone()} onloadstart={props.onloadstart.clone()} onpause={props.onpause.clone()}
637            onplay={props.onplay.clone()} onplaying={props.onplaying.clone()} onprogress={props.onprogress.clone()} onratechange={props.onratechange.clone()}
638            onseeked={props.onseeked.clone()} onseeking={props.onseeking.clone()} onstalled={props.onstalled.clone()} onsuspend={props.onsuspend.clone()}
639            ontimeupdate={props.ontimeupdate.clone()} onvolumechange={props.onvolumechange.clone()} onwaiting={props.onwaiting.clone()}>
640            { for props.children.iter() }
641        </button>
642    }
643}