sauron_core/html/
attributes.rs

1//! Create html [attributes][0]
2//!
3//! [0]: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes
4use crate::vdom;
5use crate::vdom::AttributeValue;
6use crate::vdom::Value;
7use std::borrow::Cow;
8
9pub use crate::vdom::EventCallback;
10pub use crate::vdom::Style;
11pub use crate::vdom::{key, replace, skip, skip_criteria};
12pub use crate::{dom::Event, vdom::Attribute};
13pub use attribute_macros::commons::*;
14pub use attribute_macros::*;
15
16#[macro_use]
17mod attribute_macros;
18
19/// A helper function which creates a style attribute by assembling the tuples into a string for the style value.
20/// # Example
21/// ```rust
22/// use sauron::{*, html::attributes::styles};
23///
24/// let html:Node<()> = div(vec![styles([("display", "flex"), ("flex-direction", "row")])], vec![]);
25/// ```
26/// is the same way of writing
27/// ```rust
28/// use sauron::*;
29/// use sauron::html::attributes::styles;
30///
31/// let html: Node<()> = div(vec![style!{"display":"flex","flex-direction":"row"}],vec![]);
32/// ```
33pub fn styles<MSG>(
34    pairs: impl IntoIterator<Item = (impl Into<Cow<'static, str>>, impl Into<Value>)>,
35) -> Attribute<MSG> {
36    let styles = pairs
37        .into_iter()
38        .map(|(key, value)| Style::new(key, Into::<Value>::into(value)));
39    vdom::attr("style", AttributeValue::from_styles(styles))
40}
41
42/// A helper function to build styles by accepting pairs
43pub fn styles_values<MSG>(
44    pairs: impl IntoIterator<Item = (impl Into<Cow<'static, str>>, impl Into<Value>)>,
45) -> Attribute<MSG> {
46    let styles = pairs.into_iter().map(|(key, value)| Style::new(key, value));
47    vdom::attr("style", AttributeValue::from_styles(styles))
48}
49
50/// A helper function which creates a style attribute by assembling only the parts that passed the
51/// boolean flag.
52/// # Examples
53/// ```rust
54/// use sauron::*;
55///
56/// let is_active = true;
57/// let display:Attribute<()> = styles_flag([
58///         ("display", "block", is_active),
59///         ("display", "none", !is_active),
60///     ]);
61/// ```
62/// This could also be written as
63/// ```rust
64/// use sauron::{*, html::attributes::styles};
65///
66/// let is_active = true;
67/// let display:Attribute<()> =
68///     styles([("display", if is_active { "block" }else{ "none" })]);
69/// ```
70pub fn styles_flag<MSG>(
71    trio: impl IntoIterator<Item = (impl Into<Cow<'static, str>>, impl Into<Value>, bool)>,
72) -> Attribute<MSG> {
73    let styles = trio.into_iter().filter_map(|(key, value, flag)| {
74        if flag {
75            Some(Style::new(key, value))
76        } else {
77            None
78        }
79    });
80    vdom::attr("style", AttributeValue::from_styles(styles))
81}
82
83/// A helper function which takes an array of tuple of class and a flag. The final class is
84/// assembled using only the values that has a flag which evaluates to true.
85/// # Examples
86/// ```rust
87/// use sauron::*;
88/// let is_hidden = true;
89/// let has_error = true;
90///
91/// let line:Attribute<()> = classes_flag([
92///        ("dashed", is_hidden),
93///        ("error", has_error),
94///    ]);
95/// ```
96pub fn classes_flag<MSG>(
97    pair: impl IntoIterator<Item = (impl Into<Value>, bool)>,
98) -> Attribute<MSG> {
99    let class_list = pair
100        .into_iter()
101        .filter_map(|(class, flag)| if flag { Some(class.into()) } else { None });
102
103    classes(class_list)
104}
105
106/// a helper function to add multiple classes to a node
107/// # Examples
108///
109/// ```rust
110/// use sauron::{*,html::attributes::classes};
111///
112/// let html: Node<()> =
113///    div(vec![classes(["dashed", "error"])], vec![]);
114/// ```
115pub fn classes<MSG>(class_list: impl IntoIterator<Item = impl Into<Value>>) -> Attribute<MSG> {
116    let class_values = class_list
117        .into_iter()
118        .map(|v| AttributeValue::from(v.into()));
119
120    Attribute::with_multiple_values(None, "class", class_values)
121}
122
123/// A helper function for setting attributes with no values such as checked
124/// in checkbox input type
125/// This is best called to be appended to the node since this
126/// returns an array of attributes which doesn't play well with the others
127/// # Examples
128/// ```rust
129/// use sauron::{*,html::*, html::attributes::attrs_flag};
130///
131/// let is_checked = true;
132/// let html: Node<()> =
133///     input(vec![r#type("checkbox")], vec![]).with_attributes(attrs_flag(vec![(
134///                             "checked",
135///                             "checked",
136///                             is_checked,
137///                         )]));
138/// ```
139pub fn attrs_flag<MSG>(
140    trio: impl IntoIterator<Item = (&'static str, impl Into<Value>, bool)>,
141) -> impl IntoIterator<Item = Attribute<MSG>> {
142    trio.into_iter().filter_map(|(key, value, flag)| {
143        if flag {
144            Some(attr(key, value.into()))
145        } else {
146            None
147        }
148    })
149}
150
151/// Set the attribute of this element if value is Some, empty attribute otherwise
152/// # Examples
153/// ```rust
154/// use sauron::{*, html::attributes::maybe_attr};
155///
156/// let width = Some(10);
157/// let html: Node<()> = button(vec![maybe_attr("width", width)], vec![]);
158/// let expected = r#"<button width="10"></button>"#;
159/// assert_eq!(expected, html.render_to_string());
160///
161/// let width = None::<usize>;
162/// let html: Node<()> = button(vec![maybe_attr("width", width)], vec![]);
163/// let expected = r#"<button></button>"#;
164/// assert_eq!(expected, html.render_to_string());
165/// ```
166pub fn maybe_attr<MSG>(
167    name: vdom::AttributeName,
168    value: Option<impl Into<Value>>,
169) -> Attribute<MSG> {
170    if let Some(value) = value {
171        attr(name, value)
172    } else {
173        empty_attr()
174    }
175}
176
177/// set the checked value, used checkbox and radio buttons
178/// # Examples
179/// ```rust
180/// use sauron::*;
181///
182/// let html: Node<()> =
183///     input(vec![r#type("checkbox"), checked(true)], vec![]);
184/// ```
185pub fn checked<MSG>(is_checked: bool) -> Attribute<MSG> {
186    if is_checked {
187        #[cfg(not(feature = "with-dom"))]
188        {
189            attr("checked", "checked")
190        }
191        #[cfg(feature = "with-dom")]
192        {
193            attr("checked", true)
194        }
195    } else {
196        empty_attr()
197    }
198}
199
200/// set whether an element is disabled or not
201/// # Examples
202/// ```rust
203/// use sauron::{*, html::*, html::attributes::*};
204///
205/// let html: Node<()> =
206///     input(vec![r#type("checkbox"), disabled(true)], vec![]);
207/// ```
208pub fn disabled<MSG>(is_disabled: bool) -> Attribute<MSG> {
209    if is_disabled {
210        attr("disabled", true)
211    } else {
212        empty_attr()
213    }
214}
215
216/// set whether an element, ie: details, that is the contents of the
217/// details are currently visible
218pub fn open<MSG>(is_open: bool) -> Attribute<MSG> {
219    if is_open {
220        attr("open", true)
221    } else {
222        empty_attr()
223    }
224}
225
226/// focus the html element
227/// # Examples
228/// ```rust
229/// use sauron::{*, html::*, html::attributes::*};
230///
231/// let editor:Node<()> = textarea(vec![focus(true)], vec![]);
232/// ```
233pub fn focus<MSG>(is_focus: bool) -> Attribute<MSG> {
234    attr("focus", is_focus)
235}
236
237/// a utility function to convert simple value into attribute
238/// # Examples
239/// ```rust
240/// use sauron::{*,html::attributes::attr};
241///
242/// let data_id: Attribute<()> = attr("data-id", 42);
243/// ```
244pub fn attr<MSG>(att: &'static str, v: impl Into<Value>) -> Attribute<MSG> {
245    vdom::attr(att, AttributeValue::from(v.into()))
246}
247
248/// a utility function to return create an empty attr, useful for cases where branch expression
249/// need to return an attribute which otherwise it can not produce
250/// example:
251/// ```rust
252/// use sauron::*;
253/// use sauron::html::attributes::empty_attr;
254///
255/// let img_title = Some("this is the image");
256/// let result: Attribute<()> = if let Some(img_title) = img_title{
257///     title(img_title)
258/// }
259/// else{
260///     empty_attr()
261/// };
262/// assert_eq!(title("this is the image"), result);
263/// ```
264pub fn empty_attr<MSG>() -> Attribute<MSG> {
265    vdom::attr("", AttributeValue::Empty)
266}