duid_core/core/html/attributes/
mod.rs

1
2#[macro_use]
3mod attribute_macros;
4mod attribute_value;
5mod style;
6mod attribute;
7
8use crate::core::{
9    events::Event,
10    events::listener::Listener
11};
12pub use attribute_macros::*;
13pub use attribute_value::AttributeValue;
14pub use jss::Value;
15pub use style::Style;
16pub use attribute::*;
17
18
19pub fn style<MSG>(
20    style_name: &'static str,
21    value: impl Into<Value>,
22) -> Attribute<MSG> {
23    attr(
24        style_name,
25        AttributeValue::from_styles([Style::new("", value.into())]),
26    )
27}
28
29pub fn styles<MSG>(
30    pairs: impl IntoIterator<Item = (impl ToString, impl Into<Value>)>,
31) -> Attribute<MSG> {
32    let styles = pairs.into_iter().map(|(key, value)| {
33        Style::new(key.to_string(), Into::<Value>::into(value))
34    });
35    attr("style", AttributeValue::from_styles(styles))
36}
37
38pub fn styles_values<MSG>(
39    pairs: impl IntoIterator<Item = (impl ToString, impl Into<Value>)>,
40) -> Attribute<MSG> {
41    let styles = pairs
42        .into_iter()
43        .map(|(key, value)| Style::new(key.to_string(), value));
44    attr("style", AttributeValue::from_styles(styles))
45}
46
47
48pub fn styles_flag<MSG>(
49    trio: impl IntoIterator<Item = (impl ToString, impl Into<Value>, bool)>,
50) -> Attribute<MSG> {
51    let styles = trio.into_iter().filter_map(|(key, value, flag)| {
52        if flag {
53            Some(Style::new(key, value))
54        } else {
55            None
56        }
57    });
58    attr("style", AttributeValue::from_styles(styles))
59}
60
61
62pub fn classes_flag<MSG>(
63    pair: impl IntoIterator<Item = (impl ToString, bool)>,
64) -> Attribute<MSG> {
65    let class_list = pair.into_iter().filter_map(|(class, flag)| {
66        if flag {
67            Some(class.to_string())
68        } else {
69            None
70        }
71    });
72
73    classes(class_list)
74}
75
76
77pub fn classes<MSG>(
78    class_list: impl IntoIterator<Item = impl ToString>,
79) -> Attribute<MSG> {
80    let class_values: Vec<_> = class_list
81        .into_iter()
82        .map(|v| AttributeValue::from_value(Value::from(v.to_string()))).collect();
83
84    Attribute::with_multiple_values(None, "class", &class_values)
85}
86
87
88pub fn class_namespaced<MSG>(
89    namespace: impl ToString,
90    class_names: impl ToString,
91) -> Attribute<MSG> {
92    class(jss::class_namespaced(namespace, class_names))
93}
94
95
96pub fn classes_flag_namespaced<MSG>(
97    namespace: impl ToString,
98    pair: impl IntoIterator<Item = (impl ToString, bool)>,
99) -> Attribute<MSG> {
100    let class_list = pair.into_iter().filter_map(|(class_name, flag)| {
101        if flag {
102            Some(jss::class_namespaced(namespace.to_string(), class_name))
103        } else {
104            None
105        }
106    });
107    classes(class_list)
108}
109
110
111pub fn attrs_flag<MSG>(
112    trio: impl IntoIterator<Item = (&'static str, impl Into<Value>, bool)>,
113) -> impl IntoIterator<Item = Attribute<MSG>> {
114    trio.into_iter().filter_map(|(key, value, flag)| {
115        if flag {
116            Some(into_attr(key, value.into()))
117        } else {
118            None
119        }
120    })
121}
122
123
124pub fn maybe_attr<MSG>(
125    name: &'static str,
126    value: Option<impl Into<Value>>,
127) -> Attribute<MSG> {
128    if let Some(value) = value {
129        into_attr(name, value)
130    } else {
131        empty_attr()
132    }
133}
134
135
136pub fn checked<MSG>(is_checked: bool) -> Attribute<MSG> {
137    if is_checked {
138        #[cfg(not(feature = "with-dom"))]
139        {
140            into_attr("checked", "checked")
141        }
142        #[cfg(feature = "with-dom")]
143        {
144            into_attr("checked", true)
145        }
146    } else {
147        empty_attr()
148    }
149}
150
151
152pub fn disabled<MSG>(is_disabled: bool) -> Attribute<MSG> {
153    if is_disabled {
154        into_attr("disabled", true)
155    } else {
156        empty_attr()
157    }
158}
159
160
161pub fn inner_html<V, MSG>(inner_html: V) -> Attribute<MSG>
162where
163    V: Into<Value> + Clone,
164{
165    attr(
166        "inner_html",
167        AttributeValue::function_call(inner_html.into()),
168    )
169}
170
171
172pub fn focus<MSG>(is_focus: bool) -> Attribute<MSG> {
173    into_attr("focus", is_focus)
174}
175
176
177pub fn into_attr<V: Into<Value>, MSG>(att: &'static str, v: V) -> Attribute<MSG> {
178    attr(att, AttributeValue::from_value(v.into()))
179}
180
181
182pub fn empty_attr<MSG>() -> Attribute<MSG> {
183    attr("", AttributeValue::Empty)
184}
185
186#[doc(hidden)]
187pub(crate) fn merge_plain_attributes_values<MSG>(
188    attr_values: &[&AttributeValue<MSG>],
189) -> Option<String> {
190    let plain_values: Vec<String> = attr_values
191        .iter()
192        .flat_map(|att_value| match att_value {
193            AttributeValue::Simple(simple) => Some(simple.to_string()),
194            AttributeValue::FunctionCall(fvalue) => Some(fvalue.to_string()),
195            _ => None,
196        })
197        .collect();
198    if !plain_values.is_empty() {
199        Some(plain_values.join(" "))
200    } else {
201        None
202    }
203}
204
205#[doc(hidden)]
206pub(crate) fn merge_styles_attributes_values<MSG>(
207    attr_values: &[&AttributeValue<MSG>],
208) -> Option<String> {
209    use std::fmt::Write;
210
211    let styles_values: Vec<String> = attr_values
212        .iter()
213        .flat_map(|att_value| match att_value {
214            AttributeValue::Style(styles) => {
215                let mut style_str = String::new();
216                styles.iter().for_each(|s| {
217                    write!(style_str, "{}", s).expect("must write")
218                });
219                Some(style_str)
220            }
221            _ => None,
222        })
223        .collect();
224
225    if !styles_values.is_empty() {
226        Some(styles_values.join(" "))
227    } else {
228        None
229    }
230}
231
232pub struct SegregatedAttributes<'a, MSG> {
233    pub listeners: Vec<&'a Listener<Event, MSG>>,
234    pub plain_values: Vec<&'a AttributeValue<MSG>>,
235    pub styles: Vec<&'a AttributeValue<MSG>>,
236    pub function_calls: Vec<&'a AttributeValue<MSG>>,
237}
238
239#[doc(hidden)]
240pub(crate) fn partition_callbacks_from_plain_styles_and_func_calls<MSG>(
241    attr: &Attribute<MSG>,
242) -> SegregatedAttributes<MSG> {
243    let mut listeners = vec![];
244    let mut plain_values = vec![];
245    let mut styles = vec![];
246    let mut function_calls = vec![];
247    for av in attr.value() {
248        match av {
249            AttributeValue::Simple(_plain) => {
250                plain_values.push(av);
251            }
252            AttributeValue::FunctionCall(_call) => {
253                function_calls.push(av);
254            }
255            AttributeValue::Style(_) => {
256                styles.push(av);
257            }
258            AttributeValue::EventListener(cb) => {
259                listeners.push(cb);
260            }
261            _ => (),
262        }
263    }
264    SegregatedAttributes {
265        listeners,
266        plain_values,
267        styles,
268        function_calls,
269    }
270}