完善弹窗样式,完善主题更改,如果储存主题和当前主题相同就不储存主题使用默认主题
This commit is contained in:
parent
e52c49a346
commit
1f885b54f3
5
frontend/Cargo.lock
generated
5
frontend/Cargo.lock
generated
@ -1474,6 +1474,9 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"dioxus",
|
"dioxus",
|
||||||
"dioxus-free-icons",
|
"dioxus-free-icons",
|
||||||
|
"getrandom 0.2.15",
|
||||||
|
"js-sys",
|
||||||
|
"rand 0.8.5",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
]
|
]
|
||||||
@ -1736,8 +1739,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
"js-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -10,7 +10,10 @@ edition = "2021"
|
|||||||
dioxus = { version = "0.6.0", features = ["router"] }
|
dioxus = { version = "0.6.0", features = ["router"] }
|
||||||
dioxus-free-icons = { version = "0.9", features = ["bootstrap"] }
|
dioxus-free-icons = { version = "0.9", features = ["bootstrap"] }
|
||||||
wasm-bindgen = "0.2.99"
|
wasm-bindgen = "0.2.99"
|
||||||
web-sys = { version = "0.3.76", features = ["Window","Storage","MediaQueryList","Document"] }
|
web-sys = { version = "0.3.76", features = ["Window","Storage","MediaQueryList","Document","DomTokenList","Element","MediaQueryListEvent"] }
|
||||||
|
js-sys = "0.3.76"
|
||||||
|
rand = "0.8.5"
|
||||||
|
getrandom = { version = "0.2", features = ["js"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["dioxus/web"]
|
default = ["dioxus/web"]
|
||||||
|
@ -1,11 +1,18 @@
|
|||||||
.light{
|
.light{
|
||||||
--accent-color:#3F51B5;
|
--accent-color:#3F51B5;
|
||||||
}
|
color:black;
|
||||||
.light .box{
|
|
||||||
background:
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark{
|
.dark{
|
||||||
--accent-color:#4d648d;
|
--accent-color:#4d648d;
|
||||||
|
color:white;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark{
|
||||||
|
background-color:black;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.light{
|
||||||
|
background-color:white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -562,30 +562,76 @@ video {
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.right-1 {
|
|
||||||
right: 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right-8 {
|
.right-8 {
|
||||||
right: 2rem;
|
right: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-1 {
|
|
||||||
top: 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-10 {
|
.top-10 {
|
||||||
top: 2.5rem;
|
top: 2.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.float-end {
|
.top-\[0\.06rem\] {
|
||||||
float: inline-end;
|
top: 0.06rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.m-2 {
|
||||||
|
margin: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ml-1 {
|
||||||
|
margin-left: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mr-2 {
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mt-2 {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.h-1 {
|
||||||
|
height: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.w-\[20rem\] {
|
.w-\[20rem\] {
|
||||||
width: 20rem;
|
width: 20rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.min-w-0 {
|
||||||
|
min-width: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-shrink-0 {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes progress {
|
||||||
|
0% {
|
||||||
|
width: 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-progress {
|
||||||
|
animation: 5s progress linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.items-center {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.justify-between {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
.truncate {
|
.truncate {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
@ -596,18 +642,27 @@ video {
|
|||||||
border-radius: 0.25rem;
|
border-radius: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.border-accent {
|
.border-2 {
|
||||||
border-color: var(--accent-color);
|
border-width: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg-yellow-200 {
|
.\!border-none {
|
||||||
|
border-style: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-red-500 {
|
||||||
|
--tw-border-opacity: 1;
|
||||||
|
border-color: rgb(239 68 68 / var(--tw-border-opacity, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-white {
|
||||||
--tw-bg-opacity: 1;
|
--tw-bg-opacity: 1;
|
||||||
background-color: rgb(254 240 138 / var(--tw-bg-opacity, 1));
|
background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
.px-2 {
|
.px-3 {
|
||||||
padding-left: 0.5rem;
|
padding-left: 0.75rem;
|
||||||
padding-right: 0.5rem;
|
padding-right: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.py-3 {
|
.py-3 {
|
||||||
@ -615,29 +670,18 @@ video {
|
|||||||
padding-bottom: 0.75rem;
|
padding-bottom: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-base {
|
.text-gray-300 {
|
||||||
font-size: 1rem;
|
--tw-text-opacity: 1;
|
||||||
line-height: 1.5rem;
|
color: rgb(209 213 219 / var(--tw-text-opacity, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-lg {
|
.hover\:text-accent:hover {
|
||||||
font-size: 1.125rem;
|
color: var(--accent-color);
|
||||||
line-height: 1.75rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.backdrop-blur-xl {
|
.dark\:text-gray-800:is(.dark *) {
|
||||||
--tw-backdrop-blur: blur(24px);
|
--tw-text-opacity: 1;
|
||||||
-webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);
|
color: rgb(31 41 55 / var(--tw-text-opacity, 1));
|
||||||
backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hover\:border-2:hover {
|
|
||||||
border-width: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark\:bg-sky-500:is(.dark *) {
|
|
||||||
--tw-bg-opacity: 1;
|
|
||||||
background-color: rgb(14 165 233 / var(--tw-bg-opacity, 1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media not all and (min-width: 640px) {
|
@media not all and (min-width: 640px) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use super::error::{CustomErrorInto, CustomResult};
|
use super::error::{CustomErrorInto, CustomResult};
|
||||||
use web_sys::{window, Document, MediaQueryList, Storage, Window};
|
use web_sys::{window, Document, Element, Storage, Window};
|
||||||
|
|
||||||
fn get_window() -> CustomResult<Window> {
|
fn get_window() -> CustomResult<Window> {
|
||||||
Ok(window().ok_or("浏览器window对象不存在")?)
|
Ok(window().ok_or("浏览器window对象不存在")?)
|
||||||
@ -8,7 +8,18 @@ fn get_window() -> CustomResult<Window> {
|
|||||||
fn get_storage() -> CustomResult<Storage> {
|
fn get_storage() -> CustomResult<Storage> {
|
||||||
get_window()?
|
get_window()?
|
||||||
.local_storage()?
|
.local_storage()?
|
||||||
.ok_or("浏览器不支持localStorage".into_custom_error())
|
.ok_or("获取浏览器Storge对象失败".into_custom_error())
|
||||||
|
}
|
||||||
|
fn get_document() -> CustomResult<Document> {
|
||||||
|
get_window()?
|
||||||
|
.document()
|
||||||
|
.ok_or("获取浏览器Document对象失败".into_custom_error())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_element(ele_name: &str) -> CustomResult<Element> {
|
||||||
|
get_document()?
|
||||||
|
.query_selector(ele_name)?
|
||||||
|
.ok_or(format!("获取元素{}失败", ele_name).into_custom_error())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_local_storage_value(key: &str) -> CustomResult<String> {
|
pub fn get_local_storage_value(key: &str) -> CustomResult<String> {
|
||||||
@ -21,6 +32,10 @@ pub fn set_local_storage_value(key: &str, value: &str) -> CustomResult<()> {
|
|||||||
Ok(get_storage()?.set_item(key, value)?)
|
Ok(get_storage()?.set_item(key, value)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn remove_local_storage_value(key: &str) -> CustomResult<()> {
|
||||||
|
Ok(get_storage()?.remove_item(key)?)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_media_theme() -> CustomResult<String> {
|
pub fn get_media_theme() -> CustomResult<String> {
|
||||||
let media_query = get_window()?
|
let media_query = get_window()?
|
||||||
.match_media("(prefers-color-scheme: dark)")?
|
.match_media("(prefers-color-scheme: dark)")?
|
||||||
@ -32,13 +47,10 @@ pub fn get_media_theme() -> CustomResult<String> {
|
|||||||
Ok("light".to_string())
|
Ok("light".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_element_class(ele_name: &str, class_name: &str) -> CustomResult<()> {
|
pub fn add_element_class(ele_name: &str, class_name: &str) -> CustomResult<()> {
|
||||||
get_window()?
|
Ok(get_element(ele_name)?.class_list().add_1(class_name)?)
|
||||||
.document()
|
}
|
||||||
.ok_or("浏览器document对象不存在".into_custom_error())?
|
|
||||||
.query_selector(ele_name)?
|
pub fn remove_element_class(ele_name: &str, class_name: &str) -> CustomResult<()> {
|
||||||
.ok_or(format!("获取元素{}失败", ele_name).into_custom_error())?
|
Ok(get_element(ele_name)?.class_list().remove_1(class_name)?)
|
||||||
.set_class_name(class_name);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
9
frontend/src/common/helps.rs
Normal file
9
frontend/src/common/helps.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
use rand::seq::SliceRandom;
|
||||||
|
|
||||||
|
pub fn generate_random_string(length: usize) -> String {
|
||||||
|
let charset = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
(0..length)
|
||||||
|
.map(|_| *charset.choose(&mut rng).unwrap() as char)
|
||||||
|
.collect()
|
||||||
|
}
|
@ -1,2 +1,3 @@
|
|||||||
pub mod dom;
|
pub mod dom;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
pub mod helps;
|
||||||
|
@ -3,4 +3,4 @@ pub use navbar::Navbar;
|
|||||||
pub mod theme_toggle;
|
pub mod theme_toggle;
|
||||||
pub use theme_toggle::Toggle;
|
pub use theme_toggle::Toggle;
|
||||||
pub mod notification;
|
pub mod notification;
|
||||||
pub use notification::Message;
|
pub use notification::Toast;
|
||||||
|
@ -1,55 +1,155 @@
|
|||||||
use crate::common::error::{CustomErrorInto, CustomResult};
|
use crate::common::error::{CustomErrorInto, CustomResult};
|
||||||
|
use crate::common::helps::generate_random_string;
|
||||||
|
use crate::Route;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
use dioxus_free_icons::icons::bs_icons::{BsCheckCircle, BsInfoCircle, BsXCircle, BsXLg};
|
||||||
use dioxus_free_icons::Icon;
|
use dioxus_free_icons::Icon;
|
||||||
use dioxus_free_icons::icons::bs_icons::BsXLg;
|
|
||||||
|
|
||||||
#[derive(PartialEq, Clone)]
|
#[derive(PartialEq, Clone)]
|
||||||
pub enum MessageType {
|
pub struct NotificationProvider {
|
||||||
|
pub notifications: Vec<NotificationProps>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Clone)]
|
||||||
|
pub enum NotificationType {
|
||||||
Info,
|
Info,
|
||||||
Warn,
|
|
||||||
Error,
|
Error,
|
||||||
Success,
|
Success,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Props, Clone)]
|
#[derive(PartialEq, Props, Clone)]
|
||||||
pub struct MessageProps {
|
pub struct NotificationProps {
|
||||||
#[props(default="wight".to_string())]
|
|
||||||
color: String,
|
|
||||||
#[props(default="".to_string())]
|
#[props(default="".to_string())]
|
||||||
title: String,
|
title: String,
|
||||||
#[props(default="".to_string())]
|
#[props(default="".to_string())]
|
||||||
message: String,
|
content: String,
|
||||||
#[props(default=3)]
|
#[props(default = 5)]
|
||||||
time: u64,
|
time: u64,
|
||||||
|
#[props(default=NotificationType::Info)]
|
||||||
|
r#type: NotificationType,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Clone)]
|
||||||
|
struct NoticationColorMatching {
|
||||||
|
color: String,
|
||||||
|
icon: Element,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_color_matching(notification_type: &NotificationType) -> NoticationColorMatching {
|
||||||
|
match notification_type {
|
||||||
|
NotificationType::Info => NoticationColorMatching {
|
||||||
|
color: String::from("rgba(0,168,91,0.85)"),
|
||||||
|
icon: rsx! {
|
||||||
|
Icon {
|
||||||
|
icon: BsInfoCircle,
|
||||||
|
width:18,
|
||||||
|
height:18,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
NotificationType::Error => NoticationColorMatching {
|
||||||
|
color: String::from("rgba(225,45,57,0.85)"),
|
||||||
|
icon: rsx! {
|
||||||
|
Icon {
|
||||||
|
icon: BsXCircle,
|
||||||
|
width:18,
|
||||||
|
height:18,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
NotificationType::Success => NoticationColorMatching {
|
||||||
|
color: String::from("rgba(38,131,255,0.85)"),
|
||||||
|
icon: rsx! {
|
||||||
|
Icon {
|
||||||
|
icon: BsCheckCircle,
|
||||||
|
width:18,
|
||||||
|
height:18,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Message(props: MessageProps) -> Element {
|
pub fn Notification() -> Element {
|
||||||
|
let notifications = use_context::<Signal<NotificationProvider>>()().notifications;
|
||||||
return rsx! {
|
return rsx! {
|
||||||
div {
|
div {
|
||||||
class: "w-[20rem] px-2 py-3 absolute right-8 top-10 backdrop-blur-xl max-sm:w-[12rem] hover:border-2 border-accent rounded ",
|
class: "w-[20rem] absolute right-8 top-10 max-sm:w-[12rem]",
|
||||||
style: "background-color:{props.color}",
|
{notifications.iter().map(|item| {
|
||||||
|
let color_matching=get_color_matching(&item.r#type);
|
||||||
|
rsx!{
|
||||||
div {
|
div {
|
||||||
|
id:format!("notification-{}",generate_random_string(10)),
|
||||||
class:"text-lg",
|
class:"rounded px-3 py-3 m-2 !border-none text-gray-300 dark:text-gray-800 ",
|
||||||
|
style:format!("background-color:{}",color_matching.color),
|
||||||
div {
|
div {
|
||||||
class:"float-end relative top-1 right-1",
|
div {
|
||||||
|
class:"flex items-center justify-between",
|
||||||
|
div {
|
||||||
|
onclick:|e|{
|
||||||
|
|
||||||
|
},
|
||||||
|
class:"mr-2 relative top-[0.06rem] hover:text-accent",
|
||||||
|
{color_matching.icon},
|
||||||
|
}
|
||||||
|
div {
|
||||||
|
class:"truncate min-w-0",
|
||||||
|
{item.title.clone()}
|
||||||
|
}
|
||||||
|
div {
|
||||||
|
class:"ml-1 flex-shrink-0",
|
||||||
Icon{
|
Icon{
|
||||||
icon:BsXLg,
|
icon:BsXLg,
|
||||||
|
height:18,
|
||||||
|
width:18
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
div {
|
div {
|
||||||
class:"truncate",
|
class:"mt-2 h-1 bg-white animate-progress",
|
||||||
"我是标题水水水水水水水水水水水水水水水水水水水水水sssssssssssss水水水ssssssssssssssss"
|
style: format!("animation-duration: {}s", item.time),
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
div {
|
|
||||||
class:"text-base",
|
|
||||||
"我是水水水水水水水水水ssssssssssssssssssssss水水水水内容"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
})}
|
||||||
|
}
|
||||||
|
Outlet::<Route> {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Toast();
|
||||||
|
|
||||||
|
impl Toast {
|
||||||
|
pub fn show(props: NotificationProps) {
|
||||||
|
let mut notification_context = use_context::<Signal<NotificationProvider>>();
|
||||||
|
let mut new_provider = notification_context().clone();
|
||||||
|
new_provider.notifications.push(props);
|
||||||
|
notification_context.set(new_provider);
|
||||||
|
}
|
||||||
|
pub fn info(title: String, content: String) {
|
||||||
|
Self::show(NotificationProps {
|
||||||
|
title,
|
||||||
|
content,
|
||||||
|
time: 5,
|
||||||
|
r#type: NotificationType::Info,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
pub fn error(title: String, content: String) {
|
||||||
|
Self::show(NotificationProps {
|
||||||
|
title,
|
||||||
|
content,
|
||||||
|
time: 5,
|
||||||
|
r#type: NotificationType::Error,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
pub fn success(title: String, content: String) {
|
||||||
|
Self::show(NotificationProps {
|
||||||
|
title,
|
||||||
|
content,
|
||||||
|
time: 5,
|
||||||
|
r#type: NotificationType::Success,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
use crate::common::dom::{set_element_class, set_local_storage_value};
|
use crate::common::dom::{
|
||||||
|
add_element_class, get_local_storage_value, get_media_theme, remove_element_class,
|
||||||
|
remove_local_storage_value, set_local_storage_value,
|
||||||
|
};
|
||||||
use dioxus::{logger::tracing, prelude::*};
|
use dioxus::{logger::tracing, prelude::*};
|
||||||
use dioxus_free_icons::icons::bs_icons::BsMoonStars;
|
use dioxus_free_icons::icons::bs_icons::BsMoonStars;
|
||||||
use dioxus_free_icons::icons::bs_icons::BsSun;
|
use dioxus_free_icons::icons::bs_icons::BsSun;
|
||||||
use dioxus_free_icons::Icon;
|
use dioxus_free_icons::Icon;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct IsDark(pub String);
|
pub struct ThemeProvider(pub String);
|
||||||
|
|
||||||
#[derive(PartialEq, Props, Clone)]
|
#[derive(PartialEq, Props, Clone)]
|
||||||
pub struct ToggleProps {
|
pub struct ToggleProps {
|
||||||
@ -18,37 +22,34 @@ pub struct ToggleProps {
|
|||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Toggle(props: ToggleProps) -> Element {
|
pub fn Toggle(props: ToggleProps) -> Element {
|
||||||
let mut dark_context = use_context::<Signal<IsDark>>();
|
let mut theme_context = use_context::<Signal<ThemeProvider>>();
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
div {
|
div {
|
||||||
onclick: move |_|
|
onclick: move |_|
|
||||||
{
|
{
|
||||||
let theme;
|
let system_theme=get_media_theme().unwrap_or_else(|_|"".to_string());
|
||||||
if dark_context().0=="light" {
|
let target_theme;
|
||||||
theme="dark"
|
|
||||||
|
if theme_context().0=="light" {
|
||||||
|
target_theme="dark".to_string()
|
||||||
}else {
|
}else {
|
||||||
theme="light"
|
target_theme="light".to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
dark_context.set(IsDark(theme.to_string()));
|
let _=remove_element_class("html", &theme_context().0);
|
||||||
match set_local_storage_value("theme",theme) {
|
theme_context.set(ThemeProvider(target_theme.clone()));
|
||||||
Ok(_)=>{},
|
let _=add_element_class("html", &target_theme);
|
||||||
Err(_)=>{
|
if target_theme==system_theme {
|
||||||
tracing::error!("主题储存失败");
|
let _=remove_local_storage_value("theme");
|
||||||
},
|
}else{
|
||||||
|
let _=set_local_storage_value("theme", &target_theme);
|
||||||
}
|
}
|
||||||
match set_element_class("html",theme) {
|
|
||||||
Ok(_)=>{},
|
|
||||||
Err(e)=>{
|
|
||||||
tracing::error!("主题类名设置失败:{}",e);
|
|
||||||
},
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
,
|
,
|
||||||
{
|
{
|
||||||
match {dark_context().0.as_str()} {
|
match {theme_context().0.as_str()} {
|
||||||
"dark"=>{
|
"dark"=>{
|
||||||
rsx!(
|
rsx!(
|
||||||
Icon{
|
Icon{
|
||||||
@ -78,3 +79,17 @@ pub fn Toggle(props: ToggleProps) -> Element {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_theme() -> String {
|
||||||
|
let storage_theme = get_local_storage_value("theme");
|
||||||
|
match storage_theme {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => {
|
||||||
|
let device_theme = get_media_theme();
|
||||||
|
match device_theme {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => "light".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
use dioxus::{logger::tracing, prelude::*};
|
use dioxus::{logger::tracing, prelude::*};
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
mod common;
|
mod common;
|
||||||
use common::dom::{get_local_storage_value, get_media_theme, set_element_class};
|
use common::dom::{add_element_class, get_media_theme, remove_element_class};
|
||||||
use components::theme_toggle::IsDark;
|
use components::notification::{Notification, NotificationProvider};
|
||||||
|
use components::theme_toggle::{get_theme, ThemeProvider};
|
||||||
|
|
||||||
use components::Navbar;
|
use components::Navbar;
|
||||||
use views::Home;
|
use views::Home;
|
||||||
@ -10,12 +12,11 @@ mod components;
|
|||||||
mod views;
|
mod views;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Routable, PartialEq)]
|
#[derive(Debug, Clone, Routable, PartialEq)]
|
||||||
#[rustfmt::skip]
|
|
||||||
enum Route {
|
enum Route {
|
||||||
|
#[layout(Notification)]
|
||||||
#[layout(Navbar)]
|
#[layout(Navbar)]
|
||||||
#[route("/")]
|
#[route("/")]
|
||||||
Home {},
|
Home {},
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const FAVICON: Asset = asset!("/assets/favicon.ico");
|
const FAVICON: Asset = asset!("/assets/favicon.ico");
|
||||||
@ -28,25 +29,32 @@ fn main() {
|
|||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
fn App() -> Element {
|
fn App() -> Element {
|
||||||
use_context_provider(|| Signal::new(IsDark("light".to_string())));
|
let theme = get_theme();
|
||||||
let mut is_dark_context = use_context::<Signal<IsDark>>();
|
use_context_provider(|| Signal::new(ThemeProvider(theme.clone())));
|
||||||
|
use_context_provider(|| {
|
||||||
|
Signal::new(NotificationProvider {
|
||||||
|
notifications: Vec::new(),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
let mut is_dark_context = use_context::<Signal<ThemeProvider>>();
|
||||||
|
|
||||||
use_effect(move || {
|
use_effect(move || {
|
||||||
let theme = {
|
let _ = add_element_class("html", &theme);
|
||||||
let storage_theme = get_local_storage_value("theme");
|
let window = web_sys::window().unwrap();
|
||||||
match storage_theme {
|
let media_query = window
|
||||||
Ok(s) => s,
|
.match_media("(prefers-color-scheme: dark)")
|
||||||
Err(_) => {
|
.unwrap()
|
||||||
let device_theme = get_media_theme();
|
.unwrap();
|
||||||
match device_theme {
|
let closure = Closure::wrap(Box::new(move |_: web_sys::MediaQueryListEvent| {
|
||||||
Ok(s) => s,
|
let theme = get_media_theme().unwrap_or_else(|_| "light".to_string());
|
||||||
Err(_) => "light".to_string(),
|
let _ = remove_element_class("html", &is_dark_context().0);
|
||||||
}
|
let _ = add_element_class("html", &theme);
|
||||||
}
|
is_dark_context.set(ThemeProvider(theme));
|
||||||
}
|
}) as Box<dyn FnMut(_)>);
|
||||||
};
|
media_query
|
||||||
is_dark_context.set(IsDark(theme.clone()));
|
.add_event_listener_with_callback("change", closure.as_ref().unchecked_ref())
|
||||||
let _ = set_element_class("html", &theme);
|
.unwrap();
|
||||||
|
closure.forget();
|
||||||
});
|
});
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
|
@ -1,16 +1,21 @@
|
|||||||
|
use crate::components::Toast;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use crate::components::Message;
|
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Home() -> Element {
|
pub fn Home() -> Element {
|
||||||
rsx! {
|
rsx! {
|
||||||
Message{},
|
|
||||||
div {
|
div {
|
||||||
class:"bg-yellow-200 dark:bg-sky-500",
|
class:"border-2 border-red-500",
|
||||||
"hello,world"
|
"hello,world"
|
||||||
ul {
|
ul {
|
||||||
li { "nihao" }
|
li { "nihao" }
|
||||||
}
|
}
|
||||||
|
button {
|
||||||
|
onclick: move |_| {
|
||||||
|
Toast::info("我是标sasadc我想二次TV要不以后牛魔题".to_string(), "我是内容".to_string())
|
||||||
|
},
|
||||||
|
"Show Notification"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,17 @@ module.exports = {
|
|||||||
colors: {
|
colors: {
|
||||||
accent: 'var(--accent-color)',
|
accent: 'var(--accent-color)',
|
||||||
},
|
},
|
||||||
|
animation:{
|
||||||
|
progress:"5s progress linear infinite"
|
||||||
|
|
||||||
|
},
|
||||||
|
keyframes:{
|
||||||
|
progress:{
|
||||||
|
'0%':{width:'0%'},
|
||||||
|
'100%':{width:'100%'}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [],
|
plugins: [],
|
||||||
|
Loading…
Reference in New Issue
Block a user