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}