2023-07-14 22:43:19 +01:00
|
|
|
#![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports))]
|
|
|
|
|
2023-06-16 04:51:33 +01:00
|
|
|
use crate::core::ElementMaybeSignal;
|
2023-09-13 19:39:56 +01:00
|
|
|
use crate::{
|
|
|
|
use_mutation_observer_with_options, watch_with_options, UseMutationObserverOptions,
|
|
|
|
WatchOptions,
|
|
|
|
};
|
2023-07-14 22:43:19 +01:00
|
|
|
use cfg_if::cfg_if;
|
2023-06-16 04:51:33 +01:00
|
|
|
use default_struct_builder::DefaultBuilder;
|
|
|
|
use leptos::*;
|
|
|
|
use std::marker::PhantomData;
|
|
|
|
use std::time::Duration;
|
2023-09-13 19:39:56 +01:00
|
|
|
use wasm_bindgen::JsCast;
|
2023-06-16 04:51:33 +01:00
|
|
|
|
|
|
|
/// Manipulate CSS variables.
|
|
|
|
///
|
|
|
|
/// ## Demo
|
|
|
|
///
|
|
|
|
/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_css_var)
|
|
|
|
///
|
|
|
|
/// ## Usage
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// # use leptos::*;
|
|
|
|
/// # use leptos_use::use_css_var;
|
|
|
|
/// #
|
|
|
|
/// # #[component]
|
2023-07-27 18:06:36 +01:00
|
|
|
/// # fn Demo() -> impl IntoView {
|
|
|
|
/// let (color, set_color) = use_css_var("--color");
|
2023-06-16 04:51:33 +01:00
|
|
|
///
|
2023-06-21 13:09:00 +02:00
|
|
|
/// set_color.set("red".to_string());
|
2023-06-16 04:51:33 +01:00
|
|
|
/// #
|
2023-07-27 18:06:36 +01:00
|
|
|
/// # view! { }
|
2023-06-16 04:51:33 +01:00
|
|
|
/// # }
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// The variable name itself can be a `Signal`.
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// # use leptos::*;
|
|
|
|
/// # use leptos_use::use_css_var;
|
|
|
|
/// #
|
|
|
|
/// # #[component]
|
2023-07-27 18:06:36 +01:00
|
|
|
/// # fn Demo() -> impl IntoView {
|
|
|
|
/// let (key, set_key) = create_signal("--color".to_string());
|
|
|
|
/// let (color, set_color) = use_css_var(key);
|
2023-06-16 04:51:33 +01:00
|
|
|
/// #
|
2023-07-27 18:06:36 +01:00
|
|
|
/// # view! { }
|
2023-06-16 04:51:33 +01:00
|
|
|
/// # }
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// You can specify the element that the variable is applied to as well as an initial value in case
|
|
|
|
/// the variable is not set yet. The option to listen for changes to the variable is also available.
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// # use leptos::*;
|
2023-07-03 15:16:22 +01:00
|
|
|
/// # use leptos::html::Div;
|
2023-06-16 04:51:33 +01:00
|
|
|
/// # use leptos_use::{use_css_var_with_options, UseCssVarOptions};
|
|
|
|
/// #
|
|
|
|
/// # #[component]
|
2023-07-27 18:06:36 +01:00
|
|
|
/// # fn Demo() -> impl IntoView {
|
|
|
|
/// let el = create_node_ref::<Div>();
|
2023-06-16 04:51:33 +01:00
|
|
|
///
|
|
|
|
/// let (color, set_color) = use_css_var_with_options(
|
|
|
|
/// "--color",
|
|
|
|
/// UseCssVarOptions::default()
|
|
|
|
/// .target(el)
|
|
|
|
/// .initial_value("#eee")
|
|
|
|
/// .observe(true),
|
|
|
|
/// );
|
|
|
|
///
|
2023-07-27 18:06:36 +01:00
|
|
|
/// view! {
|
2023-06-16 04:51:33 +01:00
|
|
|
/// <div node_ref=el>"..."</div>
|
|
|
|
/// }
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2023-07-14 22:43:19 +01:00
|
|
|
///
|
|
|
|
/// ## Server-Side Rendering
|
|
|
|
///
|
2023-07-27 18:06:36 +01:00
|
|
|
/// On the server this simply returns `create_signal(options.initial_value)`.
|
2023-06-16 04:51:33 +01:00
|
|
|
pub fn use_css_var(
|
|
|
|
prop: impl Into<MaybeSignal<String>>,
|
|
|
|
) -> (ReadSignal<String>, WriteSignal<String>) {
|
2023-07-27 18:06:36 +01:00
|
|
|
use_css_var_with_options(prop, UseCssVarOptions::default())
|
2023-06-16 04:51:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Version of [`use_css_var`] that takes a `UseCssVarOptions`. See [`use_css_var`] for how to use.
|
|
|
|
pub fn use_css_var_with_options<P, El, T>(
|
|
|
|
prop: P,
|
|
|
|
options: UseCssVarOptions<El, T>,
|
|
|
|
) -> (ReadSignal<String>, WriteSignal<String>)
|
|
|
|
where
|
|
|
|
P: Into<MaybeSignal<String>>,
|
|
|
|
El: Clone,
|
2023-07-27 18:06:36 +01:00
|
|
|
El: Into<ElementMaybeSignal<T, web_sys::Element>>,
|
2023-06-16 04:51:33 +01:00
|
|
|
T: Into<web_sys::Element> + Clone + 'static,
|
|
|
|
{
|
|
|
|
let UseCssVarOptions {
|
|
|
|
target,
|
|
|
|
initial_value,
|
|
|
|
observe,
|
|
|
|
..
|
|
|
|
} = options;
|
|
|
|
|
2023-07-27 18:06:36 +01:00
|
|
|
let (variable, set_variable) = create_signal(initial_value.clone());
|
2023-06-16 04:51:33 +01:00
|
|
|
|
2023-07-14 22:43:19 +01:00
|
|
|
cfg_if! { if #[cfg(not(feature = "ssr"))] {
|
2023-07-27 18:06:36 +01:00
|
|
|
let el_signal = (target).into();
|
2023-07-14 22:43:19 +01:00
|
|
|
let prop = prop.into();
|
2023-06-16 04:51:33 +01:00
|
|
|
|
2023-07-14 22:43:19 +01:00
|
|
|
let update_css_var = {
|
|
|
|
let prop = prop.clone();
|
|
|
|
let el_signal = el_signal.clone();
|
2023-06-16 04:51:33 +01:00
|
|
|
|
2023-07-14 22:43:19 +01:00
|
|
|
move || {
|
|
|
|
let key = prop.get_untracked();
|
2023-06-16 04:51:33 +01:00
|
|
|
|
2023-07-14 22:43:19 +01:00
|
|
|
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;
|
|
|
|
}
|
2023-06-16 04:51:33 +01:00
|
|
|
}
|
|
|
|
|
2023-07-14 22:43:19 +01:00
|
|
|
let initial_value = initial_value.clone();
|
|
|
|
set_variable.update(|var| *var = initial_value);
|
|
|
|
}
|
2023-06-16 04:51:33 +01:00
|
|
|
}
|
2023-07-14 22:43:19 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
if observe {
|
|
|
|
let update_css_var = update_css_var.clone();
|
|
|
|
let el_signal = el_signal.clone();
|
|
|
|
|
|
|
|
use_mutation_observer_with_options::<ElementMaybeSignal<T, web_sys::Element>, T, _>(
|
2023-07-27 18:06:36 +01:00
|
|
|
el_signal,
|
2023-07-14 22:43:19 +01:00
|
|
|
move |_, _| update_css_var(),
|
2023-09-13 19:39:56 +01:00
|
|
|
UseMutationObserverOptions::default()
|
|
|
|
.attribute_filter(vec!["style".to_string()]),
|
2023-07-14 22:43:19 +01:00
|
|
|
);
|
2023-06-16 04:51:33 +01:00
|
|
|
}
|
|
|
|
|
2023-07-14 22:43:19 +01:00
|
|
|
// To get around style attributes on node_refs that are not applied after the first render
|
|
|
|
set_timeout(update_css_var.clone(), Duration::ZERO);
|
2023-06-16 04:51:33 +01:00
|
|
|
|
2023-07-14 22:43:19 +01:00
|
|
|
{
|
|
|
|
let el_signal = el_signal.clone();
|
|
|
|
let prop = prop.clone();
|
2023-06-16 04:51:33 +01:00
|
|
|
|
2023-07-14 22:43:19 +01:00
|
|
|
let _ = watch_with_options(
|
2023-07-27 18:06:36 +01:00
|
|
|
move || (el_signal.get(), prop.get()),
|
2023-07-14 22:43:19 +01:00
|
|
|
move |_, _, _| update_css_var(),
|
|
|
|
WatchOptions::default().immediate(true),
|
|
|
|
);
|
|
|
|
}
|
2023-06-16 04:51:33 +01:00
|
|
|
|
2023-07-14 22:43:19 +01:00
|
|
|
let _ = watch(
|
2023-07-27 18:06:36 +01:00
|
|
|
move || variable.get(),
|
2023-07-14 22:43:19 +01:00
|
|
|
move |val, _, _| {
|
|
|
|
if let Some(el) = el_signal.get() {
|
|
|
|
let el = el.into().unchecked_into::<web_sys::HtmlElement>();
|
|
|
|
let style = el.style();
|
|
|
|
let _ = style.set_property(&prop.get_untracked(), val);
|
|
|
|
}
|
|
|
|
},
|
2023-07-24 21:16:59 +02:00
|
|
|
false,
|
2023-06-16 04:51:33 +01:00
|
|
|
);
|
2023-07-14 22:43:19 +01:00
|
|
|
}}
|
2023-06-16 04:51:33 +01:00
|
|
|
|
|
|
|
(variable, set_variable)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Options for [`use_css_var_with_options`].
|
|
|
|
#[derive(DefaultBuilder)]
|
|
|
|
pub struct UseCssVarOptions<El, T>
|
|
|
|
where
|
|
|
|
El: Clone,
|
2023-07-27 18:06:36 +01:00
|
|
|
El: Into<ElementMaybeSignal<T, web_sys::Element>>,
|
2023-06-16 04:51:33 +01:00
|
|
|
T: Into<web_sys::Element> + 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<T>,
|
|
|
|
}
|
|
|
|
|
2023-07-14 22:43:19 +01:00
|
|
|
cfg_if! { if #[cfg(feature = "ssr")] {
|
|
|
|
impl Default for UseCssVarOptions<Option<web_sys::Element>, web_sys::Element> {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
target: None,
|
|
|
|
initial_value: "".into(),
|
|
|
|
observe: false,
|
|
|
|
_marker: PhantomData,
|
|
|
|
}
|
2023-06-16 04:51:33 +01:00
|
|
|
}
|
|
|
|
}
|
2023-07-14 22:43:19 +01:00
|
|
|
} else {
|
|
|
|
impl Default for UseCssVarOptions<web_sys::Element, web_sys::Element> {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
target: document().document_element().expect("No document element"),
|
|
|
|
initial_value: "".into(),
|
|
|
|
observe: false,
|
|
|
|
_marker: PhantomData,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}}
|