) {
+ use_css_var_with_options(cx, prop, UseCssVarOptions::default())
+}
+
+/// Version of [`use_css_var`] that takes a `UseCssVarOptions`. See [`use_css_var`] for how to use.
+pub fn use_css_var_with_options(
+ cx: Scope,
+ prop: P,
+ options: UseCssVarOptions,
+) -> (ReadSignal, WriteSignal)
+where
+ P: Into>,
+ El: Clone,
+ (Scope, El): Into>,
+ T: Into + Clone + 'static,
+{
+ let UseCssVarOptions {
+ target,
+ initial_value,
+ observe,
+ ..
+ } = options;
+
+ let (variable, set_variable) = create_signal(cx, initial_value.clone());
+
+ let el_signal = (cx, target).into();
+ let prop = prop.into();
+
+ let update_css_var = {
+ let prop = prop.clone();
+ let el_signal = el_signal.clone();
+
+ move || {
+ let key = prop.get_untracked();
+
+ if let Some(el) = el_signal.get_untracked() {
+ if let Ok(Some(style)) = window().get_computed_style(&el.into()) {
+ if let Ok(value) = style.get_property_value(&key) {
+ set_variable.update(|var| *var = value.trim().to_string());
+ return;
+ }
+ }
+
+ let initial_value = initial_value.clone();
+ set_variable.update(|var| *var = initial_value);
+ }
+ }
+ };
+
+ if observe {
+ let mut init = web_sys::MutationObserverInit::new();
+ let update_css_var = update_css_var.clone();
+ let el_signal = el_signal.clone();
+
+ init.attribute_filter(&js_sys::Array::from_iter(
+ vec![JsValue::from_str("style")].into_iter(),
+ ));
+ use_mutation_observer_with_options::, T, _>(
+ cx,
+ el_signal,
+ move |_, _| update_css_var(),
+ init,
+ );
+ }
+
+ // To get around style attributes on node_refs that are not applied after the first render
+ set_timeout(update_css_var.clone(), Duration::ZERO);
+
+ {
+ let el_signal = el_signal.clone();
+ let prop = prop.clone();
+
+ let _ = watch_with_options(
+ cx,
+ move || (el_signal.get(), prop.get()),
+ move |_, _, _| update_css_var(),
+ WatchOptions::default().immediate(true),
+ );
+ }
+
+ let _ = watch(cx, variable, move |val, _, _| {
+ if let Some(el) = el_signal.get() {
+ let el = el.into().unchecked_into::();
+ let style = el.style();
+ let _ = style.set_property(&prop.get_untracked(), val);
+ }
+ });
+
+ (variable, set_variable)
+}
+
+/// Options for [`use_css_var_with_options`].
+#[derive(DefaultBuilder)]
+pub struct UseCssVarOptions
+where
+ El: Clone,
+ (Scope, El): Into>,
+ T: Into + Clone + 'static,
+{
+ /// The target element to read the variable from and set the variable on.
+ /// Defaults to the `document.documentElement`.
+ target: El,
+
+ /// The initial value of the variable before it is read. Also the default value
+ /// if the variable isn't defined on the target. Defaults to "".
+ #[builder(into)]
+ initial_value: String,
+
+ /// If `true` use a `MutationObserver` to monitor variable changes. Defaults to `false`.
+ observe: bool,
+
+ #[builder(skip)]
+ _marker: PhantomData,
+}
+
+impl Default for UseCssVarOptions {
+ fn default() -> Self {
+ Self {
+ target: document().document_element().expect("No document element"),
+ initial_value: "".into(),
+ observe: false,
+ _marker: PhantomData,
+ }
+ }
+}
diff --git a/src/utils/clonable_fn.rs b/src/utils/clonable_fn.rs
index 00a77bf..6b9ba8c 100644
--- a/src/utils/clonable_fn.rs
+++ b/src/utils/clonable_fn.rs
@@ -136,3 +136,34 @@ impl Debug for Box> {
)
}
}
+
+pub trait CloneableFnMut: FnMut() {
+ fn clone_box(&self) -> Box;
+}
+
+impl CloneableFnMut for F
+where
+ F: FnMut() + Clone + 'static,
+{
+ fn clone_box(&self) -> Box {
+ Box::new(self.clone())
+ }
+}
+
+impl Clone for Box {
+ fn clone(&self) -> Self {
+ (**self).clone_box()
+ }
+}
+
+impl Default for Box {
+ fn default() -> Self {
+ Box::new(|| {})
+ }
+}
+
+impl Debug for Box {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "Box",)
+ }
+}