Skip to main content

oino_ui/extras/
button.rs

1//! `Button` node (§4.5.2).
2
3use serde::{Deserialize, Serialize};
4
5use crate::extras::icon::Icon;
6use crate::node::{ActionId, Common, IntoNode, Node};
7use crate::text::Line;
8use serde_json::Value as Json;
9
10/// Button visual weight.
11#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
12pub enum ButtonVariant {
13    /// Primary action.
14    Primary,
15    /// Default weight.
16    #[default]
17    Secondary,
18    /// Quiet text button.
19    Ghost,
20    /// Destructive action.
21    Danger,
22}
23
24/// A button (§4.5.2). Fires its `action` with `value` (§4.6); `confirm` shows an
25/// inline y/n popover first.
26#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
27#[serde(rename_all = "camelCase")]
28pub struct Button {
29    /// Common node props (`key`, `style`, `block`, `fallback`).
30    #[serde(flatten)]
31    pub common: Common,
32    /// Button label.
33    pub label: Line,
34    /// Action id fired on click.
35    pub action: ActionId,
36    /// Action value (default `null`).
37    #[serde(default, skip_serializing_if = "Option::is_none")]
38    pub value: Option<Json>,
39    /// Visual weight.
40    #[serde(default, skip_serializing_if = "Option::is_none")]
41    pub variant: Option<ButtonVariant>,
42    /// Leading icon (required for `Danger` in the app).
43    #[serde(default, skip_serializing_if = "Option::is_none")]
44    pub icon: Option<Icon>,
45    /// Greyed out.
46    #[serde(default, skip_serializing_if = "Option::is_none")]
47    pub disabled: Option<bool>,
48    /// Ask for confirmation first.
49    #[serde(default, skip_serializing_if = "Option::is_none")]
50    pub confirm: Option<String>,
51}
52
53impl Button {
54    /// A button.
55    #[must_use]
56    pub fn new(label: impl Into<Line>, action: impl Into<ActionId>) -> Self {
57        Self {
58            common: Common::new(),
59            label: label.into(),
60            action: action.into(),
61            value: None,
62            variant: None,
63            icon: None,
64            disabled: None,
65            confirm: None,
66        }
67    }
68
69    /// Sets the action value.
70    #[must_use]
71    pub fn value(mut self, value: impl Into<Json>) -> Self {
72        self.value = Some(value.into());
73        self
74    }
75
76    /// Sets the visual weight.
77    #[must_use]
78    pub fn variant(mut self, variant: ButtonVariant) -> Self {
79        self.variant = Some(variant);
80        self
81    }
82
83    /// Sets the leading icon.
84    #[must_use]
85    pub fn icon(mut self, icon: Icon) -> Self {
86        self.icon = Some(icon);
87        self
88    }
89
90    /// Greys the button out.
91    #[must_use]
92    pub fn disabled(mut self, disabled: bool) -> Self {
93        self.disabled = Some(disabled);
94        self
95    }
96
97    /// Asks for confirmation first.
98    #[must_use]
99    pub fn confirm(mut self, confirm: impl Into<String>) -> Self {
100        self.confirm = Some(confirm.into());
101        self
102    }
103}
104
105impl IntoNode for Button {}
106impl From<Button> for Node {
107    fn from(b: Button) -> Self {
108        Node::Button(b)
109    }
110}