mirror of
https://github.com/adoyle0/leptos-use.git
synced 2025-01-23 00:59:22 -05:00
added support for Sec-CH-Prefers-Color-Scheme header.
Fixes #101 Relates to #121
This commit is contained in:
parent
b1367a9f0b
commit
26a2dc0e4b
8 changed files with 127 additions and 18 deletions
|
@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- Updated to web_sys 0.3.70 which unfortunately is breaking some things.
|
- Updated to web_sys 0.3.70 which unfortunately is breaking some things.
|
||||||
- `use_clipboard` doesn't need the unstable flags anymore.
|
- `use_clipboard` doesn't need the unstable flags anymore.
|
||||||
- `use_locale` now uses `unic_langid::LanguageIdentifier` and proper locale matching (thanks to @mondeja).
|
- `use_locale` now uses `unic_langid::LanguageIdentifier` and proper locale matching (thanks to @mondeja).
|
||||||
|
- `use_preferred_dark` and `use_color_mode` now try to read the `Sec-CH-Prefers-Color-Scheme` header in SSR.
|
||||||
|
|
||||||
## [0.11.4] - 2024-08-12
|
## [0.11.4] - 2024-08-12
|
||||||
|
|
||||||
|
|
|
@ -34,11 +34,11 @@ num = { version = "0.4", optional = true }
|
||||||
paste = "1"
|
paste = "1"
|
||||||
thiserror = "1"
|
thiserror = "1"
|
||||||
unic-langid = "0.9"
|
unic-langid = "0.9"
|
||||||
wasm-bindgen = ">=0.2.93"
|
wasm-bindgen = "=0.2.93"
|
||||||
wasm-bindgen-futures = "0.4"
|
wasm-bindgen-futures = "0.4"
|
||||||
|
|
||||||
[dependencies.web-sys]
|
[dependencies.web-sys]
|
||||||
version = ">=0.3.70"
|
version = "=0.3.70"
|
||||||
features = [
|
features = [
|
||||||
"AddEventListenerOptions",
|
"AddEventListenerOptions",
|
||||||
"BinaryType",
|
"BinaryType",
|
||||||
|
|
|
@ -21,8 +21,9 @@ log = "0.4"
|
||||||
simple_logger = "4"
|
simple_logger = "4"
|
||||||
tokio = { version = "1", features = ["full"], optional = true }
|
tokio = { version = "1", features = ["full"], optional = true }
|
||||||
tower = { version = "0.4", optional = true }
|
tower = { version = "0.4", optional = true }
|
||||||
|
tower-default-headers = { git = "https://github.com/banool/tower-default-headers-rs" }
|
||||||
tower-http = { version = "0.5", features = ["fs"], optional = true }
|
tower-http = { version = "0.5", features = ["fs"], optional = true }
|
||||||
wasm-bindgen = "0.2.92"
|
wasm-bindgen = "=0.2.93"
|
||||||
thiserror = "1.0.38"
|
thiserror = "1.0.38"
|
||||||
tracing = { version = "0.1.37", optional = true }
|
tracing = { version = "0.1.37", optional = true }
|
||||||
http = "1"
|
http = "1"
|
||||||
|
|
|
@ -2,11 +2,13 @@
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
use axum::{routing::post, Router};
|
use axum::{routing::post, Router};
|
||||||
|
use http::{HeaderMap, HeaderName, HeaderValue};
|
||||||
use leptos::logging::log;
|
use leptos::logging::log;
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use leptos_axum::{generate_route_list, LeptosRoutes};
|
use leptos_axum::{generate_route_list, LeptosRoutes};
|
||||||
use leptos_use_ssr::app::*;
|
use leptos_use_ssr::app::*;
|
||||||
use leptos_use_ssr::fileserv::file_and_error_handler;
|
use leptos_use_ssr::fileserv::file_and_error_handler;
|
||||||
|
use tower_default_headers::DefaultHeadersLayer;
|
||||||
|
|
||||||
simple_logger::init_with_level(log::Level::Info).expect("couldn't initialize logging");
|
simple_logger::init_with_level(log::Level::Info).expect("couldn't initialize logging");
|
||||||
|
|
||||||
|
@ -20,12 +22,19 @@ async fn main() {
|
||||||
let addr = leptos_options.site_addr;
|
let addr = leptos_options.site_addr;
|
||||||
let routes = generate_route_list(|| view! { <App/> });
|
let routes = generate_route_list(|| view! { <App/> });
|
||||||
|
|
||||||
|
let mut default_headers = HeaderMap::new();
|
||||||
|
let color_header = HeaderValue::from_static("Sec-CH-Prefers-Color-Scheme");
|
||||||
|
default_headers.insert(HeaderName::from_static("accept-ch"), color_header.clone());
|
||||||
|
default_headers.insert(HeaderName::from_static("vary"), color_header.clone());
|
||||||
|
default_headers.insert(HeaderName::from_static("critical-ch"), color_header);
|
||||||
|
|
||||||
// build our application with a route
|
// build our application with a route
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.route("/api/*fn_name", post(leptos_axum::handle_server_fns))
|
.route("/api/*fn_name", post(leptos_axum::handle_server_fns))
|
||||||
.leptos_routes(&leptos_options, routes, || view! { <App/> })
|
.leptos_routes(&leptos_options, routes, || view! { <App/> })
|
||||||
.fallback(file_and_error_handler)
|
.fallback(file_and_error_handler)
|
||||||
.with_state(leptos_options);
|
.with_state(leptos_options)
|
||||||
|
.layer(DefaultHeadersLayer::new(default_headers));
|
||||||
|
|
||||||
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
|
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
|
||||||
log!("listening on http://{}", &addr);
|
log!("listening on http://{}", &addr);
|
||||||
|
|
|
@ -2,7 +2,11 @@ use crate::core::url;
|
||||||
use crate::core::StorageType;
|
use crate::core::StorageType;
|
||||||
use crate::core::{ElementMaybeSignal, MaybeRwSignal};
|
use crate::core::{ElementMaybeSignal, MaybeRwSignal};
|
||||||
use crate::storage::{use_storage_with_options, UseStorageOptions};
|
use crate::storage::{use_storage_with_options, UseStorageOptions};
|
||||||
use crate::{sync_signal_with_options, use_cookie, use_preferred_dark, SyncSignalOptions};
|
use crate::utils::get_header;
|
||||||
|
use crate::{
|
||||||
|
sync_signal_with_options, use_cookie, use_preferred_dark_with_options,
|
||||||
|
SyncSignalOptions, UsePreferredDarkOptions,
|
||||||
|
};
|
||||||
use codee::string::FromToStringCodec;
|
use codee::string::FromToStringCodec;
|
||||||
use default_struct_builder::DefaultBuilder;
|
use default_struct_builder::DefaultBuilder;
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
|
@ -83,7 +87,7 @@ use wasm_bindgen::JsCast;
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// ### Cookies
|
/// ### Cookie
|
||||||
///
|
///
|
||||||
/// To persist color mode in a cookie, use `use_cookie_with_options` and specify `.cookie_enabled(true)`.
|
/// To persist color mode in a cookie, use `use_cookie_with_options` and specify `.cookie_enabled(true)`.
|
||||||
///
|
///
|
||||||
|
@ -112,9 +116,24 @@ use wasm_bindgen::JsCast;
|
||||||
///
|
///
|
||||||
/// ## Server-Side Rendering
|
/// ## Server-Side Rendering
|
||||||
///
|
///
|
||||||
/// On the server this will by default return `ColorMode::Light`. Persistence with storage is disabled.
|
/// On the server this will try to read the
|
||||||
|
/// [`Sec-CH-Prefers-Color-Scheme` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-CH-Prefers-Color-Scheme)
|
||||||
|
/// to determine the color mode. If the header is not present it will return `ColorMode::Light`.
|
||||||
|
/// Please have a look at the linked documentation above for that header to see browser support
|
||||||
|
/// as well as potential server requirements.
|
||||||
///
|
///
|
||||||
/// If `cookie_enabled` is set to `true`, cookies will be used and if present this value will be used
|
/// > If you're using `axum` you have to enable the `"axum"` feature in your Cargo.toml.
|
||||||
|
/// > In case it's `actix-web` enable the feature `"actix"`, for `spin` enable `"spin"`.
|
||||||
|
///
|
||||||
|
/// ### Bring your own header
|
||||||
|
///
|
||||||
|
/// In case you're neither using Axum, Actix nor Spin, or the default implementation is not to your liking,
|
||||||
|
/// you can provide your own way of reading the color scheme header value using the option
|
||||||
|
/// [`crate::UseColorModeOptions::ssr_color_header_getter`].
|
||||||
|
///
|
||||||
|
/// ### Cookie
|
||||||
|
///
|
||||||
|
/// If `cookie_enabled` is set to `true`, a cookie will be used and if present this value will be used
|
||||||
/// on the server as well as on the client. Please note that you have to add the `axum` or `actix`
|
/// on the server as well as on the client. Please note that you have to add the `axum` or `actix`
|
||||||
/// feature as described in [`fn@crate::use_cookie`].
|
/// feature as described in [`fn@crate::use_cookie`].
|
||||||
///
|
///
|
||||||
|
@ -151,6 +170,7 @@ where
|
||||||
emit_auto,
|
emit_auto,
|
||||||
transition_enabled,
|
transition_enabled,
|
||||||
listen_to_storage_changes,
|
listen_to_storage_changes,
|
||||||
|
ssr_color_header_getter,
|
||||||
_marker,
|
_marker,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
|
@ -162,7 +182,9 @@ where
|
||||||
])
|
])
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let preferred_dark = use_preferred_dark();
|
let preferred_dark = use_preferred_dark_with_options(UsePreferredDarkOptions {
|
||||||
|
ssr_color_header_getter,
|
||||||
|
});
|
||||||
|
|
||||||
let system = Signal::derive(move || {
|
let system = Signal::derive(move || {
|
||||||
if preferred_dark.get() {
|
if preferred_dark.get() {
|
||||||
|
@ -471,6 +493,14 @@ where
|
||||||
/// Defaults to true.
|
/// Defaults to true.
|
||||||
listen_to_storage_changes: bool,
|
listen_to_storage_changes: bool,
|
||||||
|
|
||||||
|
/// Getter function to return the string value of the
|
||||||
|
/// [`Sec-CH-Prefers-Color-Scheme`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-CH-Prefers-Color-Scheme)
|
||||||
|
/// header.
|
||||||
|
/// When you use one of the features `"axum"`, `"actix"` or `"spin"` there's a valid default
|
||||||
|
/// implementation provided.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
ssr_color_header_getter: Rc<dyn Fn() -> Option<String>>,
|
||||||
|
|
||||||
#[builder(skip)]
|
#[builder(skip)]
|
||||||
_marker: PhantomData<T>,
|
_marker: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
@ -496,6 +526,13 @@ impl Default for UseColorModeOptions<&'static str, web_sys::Element> {
|
||||||
emit_auto: false,
|
emit_auto: false,
|
||||||
transition_enabled: false,
|
transition_enabled: false,
|
||||||
listen_to_storage_changes: true,
|
listen_to_storage_changes: true,
|
||||||
|
ssr_color_header_getter: Rc::new(move || {
|
||||||
|
get_header!(
|
||||||
|
HeaderName::from_static("sec-ch-prefers-color-scheme"),
|
||||||
|
use_locale,
|
||||||
|
ssr_color_header_getter
|
||||||
|
)
|
||||||
|
}),
|
||||||
_marker: PhantomData,
|
_marker: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ use std::rc::Rc;
|
||||||
/// ### Bring your own header
|
/// ### Bring your own header
|
||||||
///
|
///
|
||||||
/// In case you're neither using Axum, Actix nor Spin, or the default implementation is not to your liking,
|
/// In case you're neither using Axum, Actix nor Spin, or the default implementation is not to your liking,
|
||||||
/// you can provide your own way of reading and writing the cookie header value using the option
|
/// you can provide your own way of reading the language header value using the option
|
||||||
/// [`crate::UseLocalesOptions::ssr_lang_header_getter`].
|
/// [`crate::UseLocalesOptions::ssr_lang_header_getter`].
|
||||||
pub fn use_locales() -> Signal<Vec<String>> {
|
pub fn use_locales() -> Signal<Vec<String>> {
|
||||||
use_locales_with_options(UseLocalesOptions::default())
|
use_locales_with_options(UseLocalesOptions::default())
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use crate::use_media_query;
|
use crate::utils::get_header;
|
||||||
|
use default_struct_builder::DefaultBuilder;
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
/// Reactive [dark theme preference](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme).
|
/// Reactive [dark theme preference](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme).
|
||||||
///
|
///
|
||||||
|
@ -20,12 +22,66 @@ use leptos::*;
|
||||||
///
|
///
|
||||||
/// ## Server-Side Rendering
|
/// ## Server-Side Rendering
|
||||||
///
|
///
|
||||||
/// On the server this functions returns a Signal that is always `false`.
|
/// On the server this will try to read the
|
||||||
|
/// [`Sec-CH-Prefers-Color-Scheme` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-CH-Prefers-Color-Scheme)
|
||||||
|
/// to determine the color mode. If the header is not present it will return `ColorMode::Light`.
|
||||||
|
/// Please have a look at the linked documentation above for that header to see browser support
|
||||||
|
/// as well as potential server requirements.
|
||||||
|
///
|
||||||
|
/// > If you're using `axum` you have to enable the `"axum"` feature in your Cargo.toml.
|
||||||
|
/// > In case it's `actix-web` enable the feature `"actix"`, for `spin` enable `"spin"`.
|
||||||
|
///
|
||||||
|
/// ### Bring your own header
|
||||||
|
///
|
||||||
|
/// In case you're neither using Axum, Actix nor Spin, or the default implementation is not to your liking,
|
||||||
|
/// you can provide your own way of reading the color scheme header value using the option
|
||||||
|
/// [`crate::UsePreferredDarkOptions::ssr_color_header_getter`].
|
||||||
///
|
///
|
||||||
/// ## See also
|
/// ## See also
|
||||||
///
|
///
|
||||||
/// * [`fn@crate::use_media_query`]
|
/// * [`fn@crate::use_media_query`]
|
||||||
/// * [`fn@crate::use_preferred_contrast`]
|
/// * [`fn@crate::use_preferred_contrast`]
|
||||||
pub fn use_preferred_dark() -> Signal<bool> {
|
pub fn use_preferred_dark() -> Signal<bool> {
|
||||||
use_media_query("(prefers-color-scheme: dark)")
|
use_preferred_dark_with_options(Default::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Version of [`fn@crate::use_preferred_dark`] that accepts a `UsePreferredDarkOptions`.
|
||||||
|
pub fn use_preferred_dark_with_options(options: UsePreferredDarkOptions) -> Signal<bool> {
|
||||||
|
#[cfg(not(feature = "ssr"))]
|
||||||
|
{
|
||||||
|
let _ = options;
|
||||||
|
|
||||||
|
crate::use_media_query("(prefers-color-scheme: dark)")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ssr")]
|
||||||
|
{
|
||||||
|
Signal::derive(move || (options.ssr_color_header_getter)() == Some("dark".to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Options for [`fn@crate::use_preferred_dark_with_options`].
|
||||||
|
#[derive(DefaultBuilder)]
|
||||||
|
pub struct UsePreferredDarkOptions {
|
||||||
|
/// Getter function to return the string value of the
|
||||||
|
/// [`Sec-CH-Prefers-Color-Scheme`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-CH-Prefers-Color-Scheme)
|
||||||
|
/// header.
|
||||||
|
/// When you use one of the features `"axum"`, `"actix"` or `"spin"` there's a valid default
|
||||||
|
/// implementation provided.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) ssr_color_header_getter: Rc<dyn Fn() -> Option<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for UsePreferredDarkOptions {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
ssr_color_header_getter: Rc::new(move || {
|
||||||
|
get_header!(
|
||||||
|
HeaderName::from_static("sec-ch-prefers-color-scheme"),
|
||||||
|
use_locale,
|
||||||
|
ssr_color_header_getter
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
macro_rules! get_header {
|
macro_rules! get_header {
|
||||||
(
|
(
|
||||||
$header_name:ident,
|
$header_name:expr,
|
||||||
$function_name:ident,
|
$function_name:ident,
|
||||||
$option_name:ident
|
$option_name:ident
|
||||||
$(,)?
|
$(,)?
|
||||||
|
@ -19,14 +19,19 @@ macro_rules! get_header {
|
||||||
);
|
);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "actix")]
|
#[cfg(feature = "actix")]
|
||||||
const $header_name: http0_2::HeaderName = http0_2::header::$header_name;
|
#[allow(unused_imports)]
|
||||||
|
use http0_2::{HeaderName, header::*};
|
||||||
#[cfg(any(feature = "axum", feature = "spin"))]
|
#[cfg(any(feature = "axum", feature = "spin"))]
|
||||||
const $header_name: http1::HeaderName = http1::header::$header_name;
|
#[allow(unused_imports)]
|
||||||
|
use http1::{HeaderName, header::*};
|
||||||
|
|
||||||
#[cfg(any(feature = "axum", feature = "actix", feature = "spin"))]
|
#[cfg(any(feature = "axum", feature = "actix", feature = "spin"))]
|
||||||
crate::utils::header($header_name)
|
{
|
||||||
|
let header_name = $header_name;
|
||||||
|
crate::utils::header(header_name)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue