css_in_rs/
style_provider.rs1use core::cell::RefCell;
2use std::{collections::btree_map::Entry, rc::Rc};
3
4#[doc_cfg(feature = "dioxus")]
5use dioxus::core::ScopeState;
6
7use doc_cfg::doc_cfg;
8
9use crate::{
10 backend::{Backend, CssGeneratorFn},
11 Classes, Theme,
12};
13
14#[derive(Clone)]
45pub struct StyleProvider<T> {
46 inner: Rc<RefCell<Inner<T>>>,
47}
48
49impl<T: Theme> StyleProvider<T> {
50 #[cfg(feature = "web-sys")]
53 pub fn quickstart_web(theme: T) -> Self {
54 let inner = Inner::quickstart_web(theme);
55 let inner = Rc::new(RefCell::new(inner));
56
57 StyleProvider { inner }
58 }
59
60 fn add_css_generator(&self, generator: CssGeneratorFn<T>) -> u64 {
61 self.inner.borrow_mut().add_css_generator(generator)
62 }
63
64 pub fn add_classes<C>(&self) -> C
68 where
69 C: Classes<Theme = T>,
70 {
71 let start = self.add_css_generator(C::generate);
72 C::new(start)
73 }
74
75 pub fn update_theme(&self, theme: T) {
78 self.inner.borrow_mut().update_theme(theme);
79 }
80
81 #[doc_cfg(feature = "dioxus")]
87 pub fn use_styles<'a, C>(&self, cx: &'a ScopeState) -> &'a C
88 where
89 C: Classes<Theme = T>,
90 {
91 cx.use_hook(|| self.add_classes())
92 }
93}
94
95struct CssGenerator<T> {
96 generator: CssGeneratorFn<T>,
97 start: u64,
98 stop: u64,
99}
100
101impl<T: Theme> CssGenerator<T> {
102 fn generate(&self, theme: &T, css: &mut String) {
103 let mut counter = self.start;
104 (self.generator)(theme, css, &mut counter);
105 assert_eq!(counter, self.stop);
106 }
107}
108
109struct Inner<T> {
110 backend: Box<dyn Backend<T>>,
111 current_theme: T,
112 generators: Vec<CssGenerator<T>>,
113 generator_to_idx: std::collections::BTreeMap<CssGeneratorFn<T>, usize>,
114 counter: u64,
115}
116
117impl<T: Theme> Inner<T> {
118 #[cfg(feature = "web-sys")]
119 pub fn quickstart_web(theme: T) -> Self {
120 let backend = crate::backend::web::WebSysBackend::quickstart();
121 Self::new_with_backend(backend, theme)
122 }
123
124 pub fn new_with_backend<B: Backend<T>>(backend: B, theme: T) -> Self {
125 let backend = Box::new(backend);
126 Self {
127 backend,
128 current_theme: theme,
129 generators: Default::default(),
130 generator_to_idx: Default::default(),
131 counter: 0,
132 }
133 }
134
135 pub fn add_css_generator(&mut self, generator: CssGeneratorFn<T>) -> u64 {
136 debug_assert_eq!(self.generator_to_idx.len(), self.generators.len());
137
138 match self.generator_to_idx.entry(generator) {
139 Entry::Vacant(vac) => {
140 vac.insert(self.generators.len());
141 }
142 Entry::Occupied(occ) => {
143 let idx = *occ.get();
144 return self.generators[idx].start;
145 }
146 }
147
148 let start = self.counter;
149 self.backend
150 .run_css_generator(generator, &self.current_theme, &mut self.counter);
151 let stop = self.counter;
152 let generator = CssGenerator {
153 generator,
154 start,
155 stop,
156 };
157
158 self.generators.push(generator);
159 start
160 }
161
162 fn update(&mut self) {
163 let mut css = String::default();
164 for generator in &self.generators {
165 generator.generate(&self.current_theme, &mut css);
166 }
167
168 self.backend.replace_all(css);
169 }
170
171 pub fn update_theme(&mut self, theme: T) {
172 if !self.current_theme.fast_cmp(&theme) {
173 self.current_theme = theme;
174 self.update();
175 }
176 }
177}