diff --git a/.vscode/tasks.json b/.vscode/tasks.json index ebca366..bb7dffa 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -24,16 +24,34 @@ { "label": "frontend-dev", "type": "shell", - "command": "dx serve --platform web", + "command": "dx serve", "options": { "cwd": "${workspaceFolder}/frontend" }, "problemMatcher": [] }, { + "label": "frontend-start", + "type": "shell", + "command": "serve .", + "options": { + "cwd": "${workspaceFolder}/frontend/target/dx/frontend/release/web/public" + }, + "problemMatcher": [] + }, + { "label": "frontend-build", "type": "shell", - "command": "dx build --platform web", + "command": "dx build --release", + "options": { + "cwd": "${workspaceFolder}/frontend" + }, + "problemMatcher": [] + }, + { + "label": "frontend-tailwind-build", + "type": "shell", + "command": "npx tailwindcss -i ./tailwind.css -o ./assets/styling/tailwind.css", "options": { "cwd": "${workspaceFolder}/frontend" }, diff --git a/frontend/Cargo.lock b/frontend/Cargo.lock index 36b04d2..268ca8f 100644 --- a/frontend/Cargo.lock +++ b/frontend/Cargo.lock @@ -1474,6 +1474,7 @@ version = "0.1.0" dependencies = [ "dioxus", "dioxus-free-icons", + "wasm-bindgen", "web-sys", ] diff --git a/frontend/Cargo.toml b/frontend/Cargo.toml index c00b694..3129f2f 100644 --- a/frontend/Cargo.toml +++ b/frontend/Cargo.toml @@ -9,11 +9,11 @@ edition = "2021" [dependencies] dioxus = { version = "0.6.0", features = ["router"] } dioxus-free-icons = { version = "0.9", features = ["bootstrap"] } -web-sys = { version = "0.3.76", features = ["Window","Storage","MediaQueryList"] } - +wasm-bindgen = "0.2.99" +web-sys = { version = "0.3.76", features = ["Window","Storage","MediaQueryList","Document"] } [features] -default = [] +default = ["dioxus/web"] web = ["dioxus/web"] desktop = ["dioxus/desktop"] mobile = ["dioxus/mobile"] diff --git a/frontend/assets/styling/blog.css b/frontend/assets/styling/blog.css deleted file mode 100644 index f27f060..0000000 --- a/frontend/assets/styling/blog.css +++ /dev/null @@ -1,8 +0,0 @@ -#blog { - margin-top: 50px; -} - -#blog a { - color: #ffffff; - margin-top: 50px; -} \ No newline at end of file diff --git a/frontend/assets/styling/navbar.css b/frontend/assets/styling/navbar.css deleted file mode 100644 index c20346d..0000000 --- a/frontend/assets/styling/navbar.css +++ /dev/null @@ -1,5 +0,0 @@ -#navbar { - display: flex; - flex-direction: row; -} - diff --git a/frontend/assets/tailwind.css b/frontend/assets/styling/tailwind.css similarity index 97% rename from frontend/assets/tailwind.css rename to frontend/assets/styling/tailwind.css index 9cf3327..7746ab2 100644 --- a/frontend/assets/tailwind.css +++ b/frontend/assets/styling/tailwind.css @@ -554,21 +554,12 @@ video { display: none; } -.static { - position: static; -} - -.bg-sky-700 { +.bg-yellow-200 { --tw-bg-opacity: 1; - background-color: rgb(3 105 161 / var(--tw-bg-opacity, 1)); + background-color: rgb(254 240 138 / var(--tw-bg-opacity, 1)); } -.bg-white { +.dark\:bg-sky-500:is(.dark *) { --tw-bg-opacity: 1; - background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1)); -} - -.text-slate-100 { - --tw-text-opacity: 1; - color: rgb(241 245 249 / var(--tw-text-opacity, 1)); + background-color: rgb(14 165 233 / var(--tw-bg-opacity, 1)); } \ No newline at end of file diff --git a/frontend/src/common/dom.rs b/frontend/src/common/dom.rs new file mode 100644 index 0000000..3070689 --- /dev/null +++ b/frontend/src/common/dom.rs @@ -0,0 +1,44 @@ +use super::error::{CustomErrorInto, CustomResult}; +use web_sys::{window, Document, MediaQueryList, Storage, Window}; + +fn get_window() -> CustomResult { + Ok(window().ok_or("浏览器window对象不存在")?) +} + +fn get_storage() -> CustomResult { + get_window()? + .local_storage()? + .ok_or("浏览器不支持localStorage".into_custom_error()) +} + +pub fn get_local_storage_value(key: &str) -> CustomResult { + get_storage()? + .get_item(key)? + .ok_or(format!("localStorage中不存在键'{}'", key).into_custom_error()) +} + +pub fn set_local_storage_value(key: &str, value: &str) -> CustomResult<()> { + Ok(get_storage()?.set_item(key, value)?) +} + +pub fn get_media_theme() -> CustomResult { + let media_query = get_window()? + .match_media("(prefers-color-scheme: dark)")? + .ok_or("查询media时发生错误".into_custom_error())? + .matches(); + if media_query { + return Ok("dark".to_string()); + } + Ok("light".to_string()) +} + +pub fn set_element_class(ele_name: &str, class_name: &str) -> CustomResult<()> { + get_window()? + .document() + .ok_or("浏览器document对象不存在".into_custom_error())? + .query_selector(ele_name)? + .ok_or(format!("获取元素{}失败", ele_name).into_custom_error())? + .set_class_name(class_name); + + Ok(()) +} diff --git a/frontend/src/common/error.rs b/frontend/src/common/error.rs index 48bf015..1dd3cc6 100644 --- a/frontend/src/common/error.rs +++ b/frontend/src/common/error.rs @@ -1,3 +1,5 @@ +use wasm_bindgen::JsValue; + #[derive(Debug)] pub struct CustomError(String); @@ -17,11 +19,26 @@ impl CustomErrorInto for &str { } } -impl From for CustomError { - fn from(error: E) -> Self { +impl From for CustomError { + fn from(error: std::io::Error) -> Self { + CustomError(error.to_string()) + } +} + +impl From for CustomError { + fn from(error: JsValue) -> Self { + CustomError( + error + .as_string() + .unwrap_or_else(|| String::from("Unknown JS error")), + ) + } +} + +impl From<&str> for CustomError { + fn from(error: &str) -> Self { CustomError(error.to_string()) } } pub type CustomResult = Result; - diff --git a/frontend/src/common/mod.rs b/frontend/src/common/mod.rs index a91e735..8cf9376 100644 --- a/frontend/src/common/mod.rs +++ b/frontend/src/common/mod.rs @@ -1 +1,2 @@ +pub mod dom; pub mod error; diff --git a/frontend/src/components/mod.rs b/frontend/src/components/mod.rs index f7e7d42..afd3043 100644 --- a/frontend/src/components/mod.rs +++ b/frontend/src/components/mod.rs @@ -1,4 +1,4 @@ -mod navbar; +pub mod navbar; pub use navbar::Navbar; -mod theme_toggle; -pub use theme_toggle::Toggle; \ No newline at end of file +pub mod theme_toggle; +pub use theme_toggle::Toggle; diff --git a/frontend/src/components/navbar.rs b/frontend/src/components/navbar.rs index 1f0c1db..2fe7730 100644 --- a/frontend/src/components/navbar.rs +++ b/frontend/src/components/navbar.rs @@ -1,13 +1,10 @@ +use super::Toggle; use crate::Route; use dioxus::prelude::*; -const NAVBAR_CSS: Asset = asset!("/assets/styling/navbar.css"); -use super::Toggle; - #[component] pub fn Navbar() -> Element { rsx! { - document::Link { rel: "stylesheet", href: NAVBAR_CSS } div { id: "navbar", diff --git a/frontend/src/components/theme_toggle.rs b/frontend/src/components/theme_toggle.rs index fdddae0..3f63707 100644 --- a/frontend/src/components/theme_toggle.rs +++ b/frontend/src/components/theme_toggle.rs @@ -1,8 +1,10 @@ +use crate::common::dom::{set_element_class, set_local_storage_value}; use dioxus::{logger::tracing, prelude::*}; use dioxus_free_icons::icons::bs_icons::BsMoonStars; use dioxus_free_icons::icons::bs_icons::BsSun; use dioxus_free_icons::Icon; -use crate::{IsDark,set_local_storage_value}; +#[derive(Debug, Clone, PartialEq)] +pub struct IsDark(pub String); #[derive(PartialEq, Props, Clone)] pub struct ToggleProps { @@ -16,24 +18,38 @@ pub struct ToggleProps { #[component] pub fn Toggle(props: ToggleProps) -> Element { - let mut dark_context=use_context::>(); + let mut dark_context = use_context::>(); rsx! { div { - onclick: move |_| + onclick: move |_| { - dark_context.set(IsDark(!dark_context().0)); - match set_local_storage_value("theme", if !dark_context().0 { "true" } else { "false" }) { + let theme; + if dark_context().0=="light" { + theme="dark" + }else { + theme="light" + }; + + dark_context.set(IsDark(theme.to_string())); + match set_local_storage_value("theme",theme) { Ok(_)=>{}, Err(_)=>{ tracing::error!("主题储存失败"); }, } + match set_element_class("html",theme) { + Ok(_)=>{}, + Err(e)=>{ + tracing::error!("主题类名设置失败:{}",e); + }, + + } } , { - match {dark_context().0} { - false=>{ + match {dark_context().0.as_str()} { + "dark"=>{ rsx!( Icon{ width:props.width, @@ -43,7 +59,7 @@ pub fn Toggle(props: ToggleProps) -> Element { } ) }, - true=>{ + _=>{ rsx!{ Icon{ width:props.width, diff --git a/frontend/src/input.css b/frontend/src/input.css deleted file mode 100644 index dc9b203..0000000 --- a/frontend/src/input.css +++ /dev/null @@ -1,4 +0,0 @@ -@import "@radix-ui/themes/styles.css"; -@tailwind base; -@tailwind components; -@tailwind utilities; \ No newline at end of file diff --git a/frontend/src/main.rs b/frontend/src/main.rs index 1621301..baab299 100644 --- a/frontend/src/main.rs +++ b/frontend/src/main.rs @@ -1,7 +1,7 @@ use dioxus::{logger::tracing, prelude::*}; -use web_sys::{window, MediaQueryList, Storage}; mod common; -use common::error::{CustomErrorInto, CustomResult}; +use common::dom::{get_local_storage_value, get_media_theme, set_element_class}; +use components::theme_toggle::IsDark; use components::Navbar; use views::Home; @@ -20,41 +20,7 @@ enum Route { const FAVICON: Asset = asset!("/assets/favicon.ico"); const GLOBAL_CSS: Asset = asset!("/assets/styling/global.css"); -const TAILWIND_CSS: Asset = asset!("/assets/tailwind.css"); - -#[derive(Debug, Clone, PartialEq)] -pub struct IsDark(bool); - -fn get_storage() -> CustomResult { - window() - .ok_or("浏览器window对象不存在".into_custom_error())? - .local_storage() - .map_err(|_| "无法访问localStorage API".into_custom_error())? - .ok_or("浏览器不支持localStorage".into_custom_error()) -} - -pub fn get_local_storage_value(key: &str) -> CustomResult { - get_storage()? - .get_item(key) - .map_err(|_| "读取localStorage时发生错误".into_custom_error())? - .ok_or(format!("localStorage中不存在键'{}'", key).into_custom_error()) -} - -pub fn set_local_storage_value(key: &str, value: &str) -> CustomResult<()> { - get_storage()? - .set_item(key, value) - .map_err(|_| format!("无法将值写入localStorage的'{}'键", key).into_custom_error()) -} - -pub fn get_media_theme() -> CustomResult { - let media_query = window() - .ok_or("浏览器window对象不存在".into_custom_error())? - .match_media("(prefers-color-scheme: dark)") - .map_err(|_| format!("读取媒体查询时发生错误").into_custom_error())? - .ok_or("查询media时发生错误".into_custom_error())? - .matches(); - Ok(media_query) -} +const TAILWIND_CSS: Asset = asset!("/assets/styling/tailwind.css"); fn main() { dioxus::launch(App); @@ -62,32 +28,33 @@ fn main() { #[component] fn App() -> Element { - use_context_provider(|| Signal::new(IsDark(false))); + use_context_provider(|| Signal::new(IsDark("light".to_string()))); let mut is_dark_context = use_context::>(); - #[cfg(target_arch = "wasm32")] - { - let _ = use_memo(move || { + use_effect(move || { + let theme = { let storage_theme = get_local_storage_value("theme"); match storage_theme { - Ok(b) => is_dark_context.set(IsDark(b == "true")), + Ok(s) => s, Err(_) => { let device_theme = get_media_theme(); match device_theme { - Ok(b) => is_dark_context.set(IsDark(b)), - Err(_) => is_dark_context.set(IsDark(false)) + Ok(s) => s, + Err(_) => "light".to_string(), } } } - }); - } + }; + is_dark_context.set(IsDark(theme.clone())); + let _ = set_element_class("html", &theme); + }); rsx! { - // Global app resources - document::Link { rel: "icon", href: FAVICON } - document::Stylesheet{ href: GLOBAL_CSS } - document::Stylesheet { href: TAILWIND_CSS } - - Router:: {} + div { + document::Link { rel: "icon", href: FAVICON } + document::Stylesheet{ href: GLOBAL_CSS } + document::Stylesheet { href: TAILWIND_CSS } + Router:: {} + } } } diff --git a/frontend/src/views/home.rs b/frontend/src/views/home.rs index 1831c90..94b2b56 100644 --- a/frontend/src/views/home.rs +++ b/frontend/src/views/home.rs @@ -3,6 +3,12 @@ use dioxus::prelude::*; #[component] pub fn Home() -> Element { rsx! { - div { "hello,world" } + div { + class:"bg-yellow-200 dark:bg-sky-500", + "hello,world" + ul { + li { "nihao" } + } + } } } diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index 2a69d58..02bbb7d 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -6,4 +6,5 @@ module.exports = { extend: {}, }, plugins: [], + darkMode: ['class'], }; diff --git a/frontend/input.css b/frontend/tailwind.css similarity index 100% rename from frontend/input.css rename to frontend/tailwind.css