popper_rs/state/
mod.rs

1//! State
2
3#[cfg(feature = "yew")]
4mod yew;
5
6#[cfg(feature = "yew")]
7pub use self::yew::*;
8
9use crate::sys;
10use js_sys::{Array, Object};
11use std::collections::HashMap;
12use std::fmt::Formatter;
13use std::ops::{Deref, DerefMut};
14use wasm_bindgen::prelude::*;
15use web_sys::Element;
16
17/// A map of styles.
18#[derive(Clone, Debug, Default, PartialEq, Eq)]
19pub struct StylesMap(pub HashMap<String, String>);
20
21impl Deref for StylesMap {
22    type Target = HashMap<String, String>;
23
24    fn deref(&self) -> &Self::Target {
25        &self.0
26    }
27}
28
29impl DerefMut for StylesMap {
30    fn deref_mut(&mut self) -> &mut Self::Target {
31        &mut self.0
32    }
33}
34
35impl std::fmt::Display for StylesMap {
36    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
37        for (k, v) in &self.0 {
38            write!(f, "{k}: {v};")?;
39        }
40        Ok(())
41    }
42}
43
44impl StylesMap {
45    /// Extend with a single key/value pair
46    pub fn extend_with(&self, key: impl Into<String>, value: impl Into<String>) -> Self {
47        let mut result = self.clone();
48        result.insert(key.into(), value.into());
49        result
50    }
51
52    /// Extend with multiple key/value pairs
53    pub fn extend_from<I, K, V>(&self, i: I) -> Self
54    where
55        I: IntoIterator<Item = (K, V)>,
56        K: Into<String>,
57        V: Into<String>,
58    {
59        let mut result = self.clone();
60        result.extend(i.into_iter().map(|(k, v)| (k.into(), v.into())));
61        result
62    }
63}
64
65#[derive(Clone, Debug, Default, PartialEq, Eq)]
66pub struct AttributesMap(pub HashMap<String, String>);
67
68impl Deref for AttributesMap {
69    type Target = HashMap<String, String>;
70
71    fn deref(&self) -> &Self::Target {
72        &self.0
73    }
74}
75
76impl DerefMut for AttributesMap {
77    fn deref_mut(&mut self) -> &mut Self::Target {
78        &mut self.0
79    }
80}
81
82/// A way to apply attributes to elements
83pub trait ApplyAttributes {
84    fn apply_attributes(&self, attributes: &AttributesMap);
85}
86
87impl ApplyAttributes for &Element {
88    fn apply_attributes(&self, attributes: &AttributesMap) {
89        for (k, v) in &attributes.0 {
90            let _ = self.set_attribute(k, v);
91        }
92    }
93}
94
95#[derive(Clone, Debug, Default, PartialEq)]
96pub struct State {
97    pub styles: Styles,
98    pub attributes: Attributes,
99}
100
101impl From<sys::State> for State {
102    fn from(value: sys::State) -> Self {
103        Self {
104            styles: value.styles().into(),
105            attributes: value.attributes().into(),
106        }
107    }
108}
109
110#[derive(Clone, Debug, Default, PartialEq, Eq)]
111pub struct Styles {
112    pub popper: StylesMap,
113    pub arrow: StylesMap,
114}
115
116impl From<sys::Styles> for Styles {
117    fn from(value: sys::Styles) -> Self {
118        Self {
119            popper: StylesMap(to_map(value.popper())),
120            arrow: StylesMap(to_map(value.arrow())),
121        }
122    }
123}
124
125#[derive(Clone, Debug, Default, PartialEq)]
126pub struct Attributes {
127    pub popper: AttributesMap,
128}
129
130impl From<sys::Attributes> for Attributes {
131    fn from(value: sys::Attributes) -> Self {
132        Self {
133            popper: AttributesMap(to_map(value.popper())),
134        }
135    }
136}
137
138fn to_map(value: JsValue) -> HashMap<String, String> {
139    let value: Object = match value.dyn_into() {
140        Err(_) => return Default::default(),
141        Ok(value) => value,
142    };
143
144    let entries = match Object::entries(&value).dyn_into::<Array>() {
145        Err(_) => return Default::default(),
146        Ok(entries) => entries,
147    };
148
149    let mut result = HashMap::new();
150
151    for entry in entries.into_iter() {
152        let key = js_sys::Reflect::get_u32(&entry, 0);
153        let value = js_sys::Reflect::get_u32(&entry, 1);
154        if let (Ok(key), Ok(value)) = (key, value) {
155            if let (Some(key), Some(value)) = (key.as_string(), value.as_string()) {
156                result.insert(key, value);
157            }
158        }
159    }
160
161    result
162}