Skip to main content

oino_ui/extras/
select.rs

1//! `Select` node (§4.5.2).
2
3use serde::{Deserialize, Serialize};
4
5use crate::node::{ActionId, Common, IntoNode, Key, Node};
6use crate::text::Line;
7
8/// One select option.
9#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
10#[serde(rename_all = "camelCase")]
11pub struct SelectOption {
12    /// Option value (sent as the action value).
13    pub value: String,
14    /// Option label.
15    pub label: Line,
16    /// Second-line description.
17    #[serde(default, skip_serializing_if = "Option::is_none")]
18    pub description: Option<String>,
19}
20
21impl SelectOption {
22    /// An option with value and label.
23    #[must_use]
24    pub fn new(value: impl Into<String>, label: impl Into<Line>) -> Self {
25        Self {
26            value: value.into(),
27            label: label.into(),
28            description: None,
29        }
30    }
31
32    /// Sets the description.
33    #[must_use]
34    pub fn description(mut self, description: impl Into<String>) -> Self {
35        self.description = Some(description.into());
36        self
37    }
38}
39
40impl From<(&str, &str)> for SelectOption {
41    fn from((value, label): (&str, &str)) -> Self {
42        Self::new(value, label)
43    }
44}
45
46/// A cycling select field (§4.5.2): `‹ value ›`; Enter opens the list.
47#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
48#[serde(rename_all = "camelCase")]
49pub struct Select {
50    /// Common node props (`key` is required for `Select`).
51    #[serde(flatten)]
52    pub common: Common,
53    /// Options.
54    pub options: Vec<SelectOption>,
55    /// Current value (wins when it changes, §4.6).
56    #[serde(default, skip_serializing_if = "Option::is_none")]
57    pub value: Option<String>,
58    /// Placeholder when nothing is selected.
59    #[serde(default, skip_serializing_if = "Option::is_none")]
60    pub placeholder: Option<String>,
61    /// Greyed out.
62    #[serde(default, skip_serializing_if = "Option::is_none")]
63    pub disabled: Option<bool>,
64    /// Action on change (the option value).
65    #[serde(default, skip_serializing_if = "Option::is_none")]
66    pub on_change: Option<ActionId>,
67}
68
69impl Select {
70    /// A select with the given `key` and options.
71    #[must_use]
72    pub fn new(
73        key: impl Into<Key>,
74        options: impl IntoIterator<Item = impl Into<SelectOption>>,
75    ) -> Self {
76        let mut common = Common::new();
77        common.key = Some(key.into());
78        Self {
79            common,
80            options: options.into_iter().map(Into::into).collect(),
81            value: None,
82            placeholder: None,
83            disabled: None,
84            on_change: None,
85        }
86    }
87
88    /// Sets the current value.
89    #[must_use]
90    pub fn value(mut self, value: impl Into<String>) -> Self {
91        self.value = Some(value.into());
92        self
93    }
94
95    /// Sets the placeholder.
96    #[must_use]
97    pub fn placeholder(mut self, placeholder: impl Into<String>) -> Self {
98        self.placeholder = Some(placeholder.into());
99        self
100    }
101
102    /// Greys the select out.
103    #[must_use]
104    pub fn disabled(mut self, disabled: bool) -> Self {
105        self.disabled = Some(disabled);
106        self
107    }
108
109    /// Sets the change action.
110    #[must_use]
111    pub fn on_change(mut self, action: impl Into<ActionId>) -> Self {
112        self.on_change = Some(action.into());
113        self
114    }
115}
116
117impl IntoNode for Select {}
118impl From<Select> for Node {
119    fn from(s: Select) -> Self {
120        Node::Select(s)
121    }
122}