rustyle_css/
reactive.rs

1//! Reactive styling support for Rustyle
2//!
3//! This module provides functionality for dynamic styles that update based on Leptos signals.
4
5#[cfg(feature = "csr")]
6use leptos::ReadSignal;
7
8/// Represents a reactive style that can update based on signals
9pub struct ReactiveStyle {
10    pub class_name: String,
11    pub base_css: String,
12}
13
14impl ReactiveStyle {
15    /// Create a new reactive style
16    pub fn new(class_name: String, base_css: String) -> Self {
17        Self {
18            class_name,
19            base_css,
20        }
21    }
22
23    /// Get the class name
24    pub fn class(&self) -> &str {
25        &self.class_name
26    }
27}
28
29/// Reactive style builder for Leptos signals
30#[cfg(feature = "csr")]
31pub struct ReactiveStyleBuilder {
32    base_css: String,
33    dynamic_props: Vec<(String, Box<dyn Fn() -> String>)>,
34}
35
36#[cfg(feature = "csr")]
37impl ReactiveStyleBuilder {
38    /// Create a new reactive style builder
39    pub fn new(base_css: &str) -> Self {
40        Self {
41            base_css: base_css.to_string(),
42            dynamic_props: Vec::new(),
43        }
44    }
45
46    /// Add a dynamic property that updates based on a signal
47    pub fn property<T: 'static + Clone + std::fmt::Display>(
48        mut self,
49        property: &str,
50        signal: ReadSignal<T>,
51    ) -> Self {
52        let prop = property.to_string();
53        self.dynamic_props.push((
54            prop.clone(),
55            Box::new(move || format!("{}: {};", prop, signal.get())),
56        ));
57        self
58    }
59
60    /// Build the reactive style
61    pub fn build(self) -> String {
62        let mut css = self.base_css.clone();
63
64        for (_, prop_fn) in &self.dynamic_props {
65            css.push_str(&prop_fn());
66            css.push_str(" ");
67        }
68
69        css
70    }
71}
72
73/// Create a reactive style from signals
74#[cfg(feature = "csr")]
75pub fn create_reactive_style<T: 'static + Clone + std::fmt::Display>(
76    base_css: &str,
77    properties: Vec<(&str, ReadSignal<T>)>,
78) -> String {
79    let mut css = base_css.to_string();
80
81    for (prop, signal) in properties {
82        css.push_str(&format!("{}: {}; ", prop, signal.get()));
83    }
84
85    css
86}
87
88/// Helper function to inject reactive styles in CSR mode
89#[cfg(all(feature = "csr", target_arch = "wasm32"))]
90pub fn inject_reactive_style(class_name: &str, css: &str) {
91    use wasm_bindgen::prelude::*;
92    use web_sys::window;
93
94    if let Some(window) = window() {
95        if let Some(document) = window.document() {
96            // Check if style element already exists for this class
97            let element_id = format!("rustyle-reactive-{}", class_name);
98            if let Ok(Some(existing)) = document.get_element_by_id(&element_id) {
99                existing.set_text_content(Some(css));
100                return;
101            }
102
103            // Create new style element
104            if let Ok(style_element) = document.create_element("style") {
105                style_element.set_id(&element_id);
106                style_element.set_text_content(Some(css));
107
108                if let Some(head) = document.head() {
109                    let _ = head.append_child(&style_element);
110                }
111            }
112        }
113    }
114}
115
116/// No-op for non-CSR builds
117#[cfg(not(all(feature = "csr", target_arch = "wasm32")))]
118pub fn inject_reactive_style(_class_name: &str, _css: &str) {
119    // No-op in non-CSR builds
120}