Skip to main content

dioxus_mind_elixir/
theme.rs

1//! Themes use the same public CSS variables as host stylesheets.
2use crate::MindMapError;
3use std::collections::BTreeMap;
4
5/// Explicit optional theme. The default component sets no theme variables, so
6/// ancestor CSS remains effective. A supplied theme takes precedence over
7/// inherited values; the component's `style` can override the theme again.
8#[derive(Clone, Debug, Default, PartialEq, Eq)]
9pub struct Theme {
10    /// Human-readable theme name.
11    pub name: String,
12    /// Main-branch colors, inherited by descendants. Empty uses host CSS.
13    pub palette: Vec<String>,
14    variables: BTreeMap<String, String>,
15}
16impl Theme {
17    /// Create a theme with no forced visual defaults.
18    pub fn new(name: impl Into<String>) -> Self {
19        Self {
20            name: name.into(),
21            ..Self::default()
22        }
23    }
24    /// Set a public CSS variable. Reject declaration separators so one value
25    /// cannot unexpectedly inject other declarations. Host `style` remains a
26    /// normal unrestricted CSS declaration string.
27    pub fn set_variable(
28        &mut self,
29        name: impl Into<String>,
30        value: impl Into<String>,
31    ) -> Result<(), MindMapError> {
32        let name = name.into();
33        let value = value.into();
34        if !name.starts_with("--dme-")
35            || name.len() <= 6
36            || !name
37                .bytes()
38                .all(|byte| byte.is_ascii_alphanumeric() || byte == b'-')
39            || value.trim().is_empty()
40            || value.contains([';', '{', '}'])
41        {
42            return Err(MindMapError::InvalidOperation(
43                "Expected a --dme-* variable and one CSS value".into(),
44            ));
45        }
46        self.variables.insert(name, value);
47        Ok(())
48    }
49    /// Variable overrides in this explicit theme.
50    pub fn variables(&self) -> &BTreeMap<String, String> {
51        &self.variables
52    }
53    pub(crate) fn css(&self) -> String {
54        self.variables
55            .iter()
56            .map(|(name, value)| format!("{name}:{value};"))
57            .collect()
58    }
59}