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}