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