使用自定义属性来实现主题,增加阅读进度

This commit is contained in:
lsy 2025-01-10 00:21:29 +08:00
parent 4cea2c3e43
commit cd67ab3f2e
11 changed files with 187 additions and 55 deletions

View File

@ -1,26 +1,20 @@
.light{
html[data-theme="light"]{
--accent-color:#3F51B5;
color:black;
background-color:white;
}
.dark{
html[data-theme="dark"]{
--accent-color:#4d648d;
color:white;
}
html.dark{
background-color:black;
}
html.light{
background-color:white;
}
@font-face {
font-family: 'AlimamaTi';
src: url('/assets/fonts/AlimamaFangYuanTiVF.ttf') format('truetype');
font-display: swap;
font-weight: 900;
font-weight: 500;
}
html {

View File

@ -558,6 +558,10 @@ video {
position: static;
}
.fixed {
position: fixed;
}
.absolute {
position: absolute;
}
@ -566,10 +570,22 @@ video {
position: relative;
}
.left-0 {
left: 0px;
}
.right-0 {
right: 0px;
}
.right-8 {
right: 2rem;
}
.top-0 {
top: 0px;
}
.top-5 {
top: 1.25rem;
}
@ -587,10 +603,6 @@ video {
margin-right: auto;
}
.mb-5 {
margin-bottom: 1.25rem;
}
.ml-1 {
margin-left: 0.25rem;
}
@ -599,6 +611,10 @@ video {
margin-right: 0.5rem;
}
.mt-14 {
margin-top: 3.5rem;
}
.mt-2 {
margin-top: 0.5rem;
}
@ -615,6 +631,10 @@ video {
display: grid;
}
.hidden {
display: none;
}
.h-1 {
height: 0.25rem;
}
@ -623,6 +643,14 @@ video {
height: 3rem;
}
.h-7 {
height: 1.75rem;
}
.h-\[999px\] {
height: 999px;
}
.w-\[20rem\] {
width: 20rem;
}
@ -639,6 +667,10 @@ video {
min-width: 0px;
}
.min-w-7 {
min-width: 1.75rem;
}
.flex-shrink-0 {
flex-shrink: 0;
}
@ -691,6 +723,10 @@ video {
justify-content: space-between;
}
.gap-1 {
gap: 0.25rem;
}
.gap-10 {
gap: 2.5rem;
}
@ -713,6 +749,10 @@ video {
border-radius: 0.25rem;
}
.rounded-full {
border-radius: 9999px;
}
.rounded-md {
border-radius: 0.375rem;
}
@ -753,6 +793,10 @@ video {
border-color: transparent;
}
.bg-accent {
background-color: var(--accent-color);
}
.bg-blue-400 {
--tw-bg-opacity: 1;
background-color: rgb(96 165 250 / var(--tw-bg-opacity, 1));
@ -788,6 +832,10 @@ video {
background-color: rgb(148 163 184 / var(--tw-bg-opacity, 1));
}
.p-2 {
padding: 0.5rem;
}
.px-3 {
padding-left: 0.75rem;
padding-right: 0.75rem;
@ -817,6 +865,11 @@ video {
line-height: 1.75rem;
}
.text-xs {
font-size: 0.75rem;
line-height: 1rem;
}
.text-blue-600 {
--tw-text-opacity: 1;
color: rgb(37 99 235 / var(--tw-text-opacity, 1));
@ -857,6 +910,10 @@ video {
transition-duration: 200ms;
}
.duration-300 {
transition-duration: 300ms;
}
.ease-in-out {
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}
@ -905,11 +962,6 @@ video {
opacity: 1;
}
.dark\:text-gray-200:is(.dark *) {
--tw-text-opacity: 1;
color: rgb(229 231 235 / var(--tw-text-opacity, 1));
}
@media not all and (min-width: 640px) {
.max-sm\:w-\[12rem\] {
width: 12rem;

View File

@ -1,23 +1,59 @@
use super::Toggle;
use crate::utils::dom::{add_element_class, get_document, remove_element_class};
use crate::Route;
use dioxus::prelude::*;
use dioxus::{logger::tracing, prelude::*};
use wasm_bindgen::{prelude::Closure, JsCast};
#[component]
pub fn Navbar() -> Element {
let mut progress_signal = use_signal(|| 0);
let mut progress_hover = use_signal(|| false);
use_effect(move || {
let window = web_sys::window().unwrap();
let document = get_document().unwrap();
let document_element = document.document_element().unwrap();
let closure: Closure<dyn FnMut(_)> = Closure::wrap(Box::new(move |_: web_sys::Event| {
if progress_hover() {
return;
}
let screen_height = document_element.scroll_height() as f64;
let inner_height = window.inner_height().unwrap().as_f64().unwrap();
let scrool_height = window.scroll_y().unwrap();
let max_scroll_height = screen_height - inner_height;
let percent = if max_scroll_height > 0.0 {
scrool_height / max_scroll_height
} else {
0.0
};
let percent = (percent.clamp(0.0, 1.0) * 100.0) as i32;
if percent > 0 {
let _ = remove_element_class("nav .progress", "hidden");
} else {
let _ = add_element_class("nav .progress", "hidden");
}
progress_signal.set(percent);
}) as Box<dyn FnMut(_)>);
document
.add_event_listener_with_callback("scroll", closure.as_ref().unchecked_ref())
.unwrap();
closure.forget();
});
rsx! {
nav {
class: "border-red-500 bg-slate-400 mb-5",
div {
nav {
class: "border-red-500 bg-slate-400 text-xl fixed top-0 left-0 right-0",
div {
class: "grid grid-cols-3 items-center text-center w-[80%] mx-auto h-12",
div {
class:"justify-self-start text-xl",
class:"justify-self-start",
Link {
to: Route::Home {},
"echoer"
}
}
div {
class:"text-xl flex gap-10 justify-center",
class:"flex gap-10 justify-center",
Link {
to: Route::Home {},
"首页"
@ -31,18 +67,46 @@ pub fn Navbar() -> Element {
"首页"
}
}
div {
class:"justify-self-end",
class:"justify-self-end flex items-center gap-1",
Toggle {
height: 35,
width: 35
height: 30,
width: 30
}
div {
class:"progress bg-accent h-7 p-2 rounded-full text-xs flex items-center justify-center min-w-7 transition-all duration-300 hidden",
onmouseenter:move|_|{
progress_hover.set(true);
},
onmouseleave:move|_|{
progress_hover.set(false);
},
onclick:move|_|{
progress_hover.set(false);
let window = web_sys::window().unwrap();
window.scroll_to_with_x_and_y(0.0, 0.0);
},
{
if progress_hover(){
"".to_string()
}
else{
if progress_signal()==100{
"返回顶部".to_string()
}
else {
progress_signal().to_string()
}
}
}
}
}
}
}
div {
class:"w-[80%] mx-auto",
class:"w-[80%] mx-auto mt-14",
Outlet::<Route> {}
}
}

View File

@ -1,11 +1,10 @@
use std::format;
use crate::utils::dom::add_element_class;
use common::helps::generate_random_string;
use crate::Route;
use common::helps::generate_random_string;
use dioxus::{logger::tracing, prelude::*};
use dioxus_free_icons::icons::bs_icons::{BsCheckCircle, BsInfoCircle, BsXCircle, BsXLg};
use dioxus_free_icons::Icon;
use std::format;
use web_sys::window;
#[derive(PartialEq, Clone)]

View File

@ -1,6 +1,6 @@
use crate::utils::dom::{
add_element_class, get_local_storage_value, get_media_theme, remove_element_class,
remove_local_storage_value, set_local_storage_value,
get_local_storage_value, get_media_theme, remove_local_storage_value, set_element_dataset,
set_local_storage_value,
};
use dioxus::{logger::tracing, prelude::*};
use dioxus_free_icons::icons::bs_icons::BsMoonStars;
@ -39,9 +39,8 @@ pub fn Toggle(props: ToggleProps) -> Element {
target_theme="light".to_string()
};
let _=remove_element_class("html", &theme_context().0);
theme_context.set(ThemeProvider(target_theme.clone()));
let _=add_element_class("html", &target_theme);
let _=set_element_dataset("html","theme", &target_theme);
if target_theme==system_theme {
let _=remove_local_storage_value("theme");
}else{

View File

@ -1,16 +1,15 @@
use dioxus::{logger::tracing, prelude::*};
use wasm_bindgen::prelude::*;
mod utils;
use wasm_bindgen::{prelude::Closure, JsCast};
mod components;
mod utils;
mod views;
use utils::dom::{add_element_class, get_media_theme, remove_element_class};
use crate::utils::error::CustomResult;
use components::notification::{Notification, NotificationProvider};
use components::theme_toggle::{get_theme, ThemeProvider};
use components::Navbar;
use utils::dom::{get_media_theme, set_element_dataset};
use views::Home;
#[derive(Debug, Clone, Routable, PartialEq)]
enum Route {
#[layout(Notification)]
@ -35,7 +34,7 @@ fn App() -> Element {
let mut is_dark_context = use_context::<Signal<ThemeProvider>>();
use_effect(move || {
let _ = add_element_class("html", &theme);
let _ = set_element_dataset("html", "theme", &theme);
let window = web_sys::window().unwrap();
let media_query = window
.match_media("(prefers-color-scheme: dark)")
@ -43,8 +42,7 @@ fn App() -> Element {
.unwrap();
let closure = Closure::wrap(Box::new(move |_: web_sys::MediaQueryListEvent| {
let theme = get_media_theme().unwrap_or_else(|_| "light".to_string());
let _ = remove_element_class("html", &is_dark_context().0);
let _ = add_element_class("html", &theme);
let _ = set_element_dataset("html", "theme", &theme);
is_dark_context.set(ThemeProvider(theme));
}) as Box<dyn FnMut(_)>);
media_query

View File

@ -10,13 +10,13 @@ fn get_storage() -> CustomResult<Storage> {
.local_storage()?
.ok_or("获取浏览器Storge对象失败".into_custom_error())
}
fn get_document() -> CustomResult<Document> {
pub fn get_document() -> CustomResult<Document> {
get_window()?
.document()
.ok_or("获取浏览器Document对象失败".into_custom_error())
}
fn get_element(ele_name: &str) -> CustomResult<Element> {
pub fn get_element(ele_name: &str) -> CustomResult<Element> {
get_document()?
.query_selector(ele_name)?
.ok_or(format!("获取元素{}失败", ele_name).into_custom_error())
@ -57,6 +57,22 @@ pub fn remove_element_class(ele_name: &str, class_name: &str) -> CustomResult<()
pub fn remove_element(ele_name: &str) -> CustomResult<()> {
let e = get_element(ele_name)?;
let _ = e.parent_node().ok_or("无法获取父节点".into_custom_error())?.remove_child(&e)?;
let _ = e
.parent_node()
.ok_or("无法获取父节点".into_custom_error())?
.remove_child(&e)?;
Ok(())
}
pub fn set_element_dataset(
ele_name: &str,
attribute_name: &str,
attribute_value: &str,
) -> CustomResult<()> {
Ok(get_element(ele_name)?
.set_attribute(&format!("data-{}", attribute_name), attribute_value)?)
}
pub fn remove_element_dataset(ele_name: &str, attribute_name: &str) -> CustomResult<()> {
Ok(get_element(ele_name)?.remove_attribute(attribute_name)?)
}

View File

@ -1,6 +1,8 @@
use wasm_bindgen::JsValue;
use common::error::CommonError;
use dioxus::prelude::RenderError;
use wasm_bindgen::JsValue;
#[derive(Debug)]
pub struct CustomError(pub String);
pub trait CustomErrorInto {
@ -13,7 +15,6 @@ impl CustomErrorInto for &str {
}
}
impl From<CommonError> for CustomError {
fn from(error: CommonError) -> Self {
CustomError(error.0)
@ -30,5 +31,10 @@ impl From<JsValue> for CustomError {
}
}
pub type CustomResult<T> = Result<T, CustomError>;
impl From<RenderError> for CustomError {
fn from(error: RenderError) -> Self {
CustomError(error.to_string())
}
}
pub type CustomResult<T> = Result<T, CustomError>;

View File

@ -5,7 +5,7 @@ use dioxus::prelude::*;
pub fn Home() -> Element {
rsx! {
div {
class:"border-2 border-red-500",
class:"border-2 border-red-500 h-[999px]",
"hello,world"
ul {
li { "nihao" }

View File

@ -24,5 +24,5 @@ module.exports = {
},
},
plugins: [],
darkMode: ['class'],
darkMode: ['data-theme="dark"'],
};

View File

@ -1,4 +1,8 @@
use surrealdb::{engine::any::{connect,Any}, Surreal,opt::auth::Root};
use surrealdb::{
engine::any::{connect, Any},
opt::auth::Root,
Surreal,
};
use crate::utils::error::CustomResult;
@ -24,7 +28,7 @@ impl Database {
Ok(Self { client })
}
pub fn get_client(&self) -> &Surreal<Any> {
pub fn get_client(&self) -> &Surreal<Any> {
&self.client
}
}