leptos_use/
use_css_var.rs1#![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports))]
2
3use crate::core::IntoElementMaybeSignal;
4use crate::{
5 UseMutationObserverOptions, WatchOptions, use_mutation_observer_with_options,
6 watch_with_options,
7};
8use default_struct_builder::DefaultBuilder;
9use leptos::prelude::*;
10use std::marker::PhantomData;
11use std::time::Duration;
12use wasm_bindgen::JsCast;
13
14pub fn use_css_var(prop: impl Into<Signal<String>>) -> (ReadSignal<String>, WriteSignal<String>) {
83 use_css_var_with_options(prop, UseCssVarOptions::default())
84}
85
86pub fn use_css_var_with_options<P, El, M>(
88 prop: P,
89 options: UseCssVarOptions<El, M>,
90) -> (ReadSignal<String>, WriteSignal<String>)
91where
92 P: Into<Signal<String>>,
93 El: Clone,
94 El: IntoElementMaybeSignal<web_sys::Element, M>,
95{
96 let UseCssVarOptions {
97 target,
98 initial_value,
99 observe,
100 ..
101 } = options;
102
103 let (variable, set_variable) = signal(initial_value.clone());
104
105 #[cfg(not(feature = "ssr"))]
106 {
107 let el_signal = target.into_element_maybe_signal();
108 let prop = prop.into();
109
110 let update_css_var = move || {
111 if let Some(el) = el_signal.get_untracked() {
112 if let Ok(Some(style)) = window().get_computed_style(&el)
113 && let Ok(value) = style.get_property_value(&prop.read_untracked())
114 {
115 set_variable.update(|var| *var = value.trim().to_string());
116 return;
117 }
118
119 let initial_value = initial_value.clone();
120 set_variable.update(|var| *var = initial_value);
121 }
122 };
123
124 if observe {
125 let update_css_var = update_css_var.clone();
126
127 use_mutation_observer_with_options(
128 el_signal,
129 move |_, _| update_css_var(),
130 UseMutationObserverOptions::default().attribute_filter(vec!["style".to_string()]),
131 );
132 }
133
134 set_timeout(update_css_var.clone(), Duration::ZERO);
136
137 let _ = watch_with_options(
138 move || (el_signal.get(), prop.get()),
139 move |_, _, _| update_css_var(),
140 WatchOptions::default().immediate(true),
141 );
142
143 Effect::watch(
144 move || variable.get(),
145 move |val, _, _| {
146 if let Some(el) = el_signal.get() {
147 let el = el.unchecked_ref::<web_sys::HtmlElement>();
148 let style = el.style();
149 let _ = style.set_property(&prop.get_untracked(), val);
150 }
151 },
152 false,
153 );
154 }
155
156 (variable, set_variable)
157}
158
159#[derive(DefaultBuilder)]
161pub struct UseCssVarOptions<El, M>
162where
163 El: IntoElementMaybeSignal<web_sys::Element, M>,
164{
165 target: El,
168
169 #[builder(into)]
172 initial_value: String,
173
174 observe: bool,
176
177 #[builder(skip)]
178 _marker: PhantomData<M>,
179}
180
181#[cfg(feature = "ssr")]
182impl<M> Default for UseCssVarOptions<Option<web_sys::Element>, M>
183where
184 Option<web_sys::Element>: IntoElementMaybeSignal<web_sys::Element, M>,
185{
186 fn default() -> Self {
187 Self {
188 target: None,
189 initial_value: "".into(),
190 observe: false,
191 _marker: PhantomData,
192 }
193 }
194}
195
196#[cfg(not(feature = "ssr"))]
197impl<M> Default for UseCssVarOptions<web_sys::Element, M>
198where
199 web_sys::Element: IntoElementMaybeSignal<web_sys::Element, M>,
200{
201 fn default() -> Self {
202 Self {
203 target: document().document_element().expect("No document element"),
204 initial_value: "".into(),
205 observe: false,
206 _marker: PhantomData,
207 }
208 }
209}