feat: Refactor comment section to correctly handle API response structure
All checks were successful
Push to Helm Chart Repository / build (push) Successful in 9s

fix: Update Gravatar URL size and improve avatar rendering logic

style: Adjust footer margin for better layout consistency

refactor: Remove old navbar component and integrate new layout structure

feat: Enhance user profile page with user header component

chore: Remove unused user profile component

fix: Update posts per page configuration for better pagination

feat: Extend device context to support system theme mode

refactor: Remove unused device hook

fix: Improve storage state hook for better error handling

i18n: Add new translations for blog home page

feat: Implement pagination component for better navigation

feat: Create theme toggle component for improved user experience

feat: Introduce responsive navbar or side layout with theme toggle

feat: Develop custom select component for better UI consistency

feat: Create user header component to display user information

chore: Add query key constants for better code maintainability
This commit is contained in:
2025-09-12 00:26:08 +08:00
parent b3e8a5ef77
commit d1d8aa529f
36 changed files with 1443 additions and 731 deletions

View File

@ -0,0 +1,88 @@
import { useDevice } from "@/contexts/device-context";
import { Sun, Moon, Monitor } from "lucide-react";
import { motion } from "motion/react";
import type React from "react";
import { cn } from "@/lib/utils";
type ThemeMode = "light" | "dark" | "system";
// PC端三状态轮换按钮
export function ThemeModeCycleButton(props: React.ButtonHTMLAttributes<HTMLButtonElement> & { mode: ThemeMode; setMode: (m: ThemeMode) => void }) {
const { mode, setMode, className, style, onClick, ...rest } = props;
const nextMode = (mode: ThemeMode): ThemeMode => {
if (mode === "light") return "dark";
if (mode === "dark") return "system";
return "light";
};
const icon = mode === "light" ? <Sun className="w-4 h-4" /> : mode === "dark" ? <Moon className="w-4 h-4" /> : <Monitor className="w-4 h-4" />;
const label = mode.charAt(0).toUpperCase() + mode.slice(1);
const baseCls = "flex items-center gap-2 px-2 py-2 rounded-full bg-muted hover:bg-accent border border-input text-sm font-medium transition-all";
const mergedClassName = cn(baseCls, className);
return (
<button
className={mergedClassName}
style={style}
onClick={(e) => {
setMode(nextMode(mode));
onClick?.(e);
}}
title={`切换主题(当前:${label}`}
{...rest}
>
{icon}
</button>
);
}
// 移动端:横向按钮组
export function ThemeModeSegmented(props: React.HTMLAttributes<HTMLDivElement> & { mode: ThemeMode; setMode: (m: ThemeMode) => void }) {
const { mode, setMode, className, style, ...rest } = props;
const modes: { value: ThemeMode; icon: React.ReactNode; label: string }[] = [
{ value: "light", icon: <Sun className="w-4 h-4" />, label: "Light" },
{ value: "system", icon: <Monitor className="w-4 h-4" />, label: "System" },
{ value: "dark", icon: <Moon className="w-4 h-4" />, label: "Dark" },
];
const activeIndex = modes.findIndex((m) => m.value === mode);
const baseCls = "relative inline-flex bg-muted rounded-full p-1 gap-1 overflow-hidden";
return (
<div className={cn("theme-mode-segmented-wrapper", className)} style={style} {...rest}>
<div className={baseCls}>
{/* 滑动高亮块 */}
<motion.div
layout
transition={{ type: "spring", stiffness: 400, damping: 30 }}
className="absolute w-12 h-8 rounded-full bg-white/70 shadow-sm z-1 top-1"
style={{
left: `calc(0.25rem + ${activeIndex} * (3rem + 0.25rem))`,
}}
/>
{modes.map((m) => (
<button
key={m.value}
className={cn(
"relative flex items-center justify-center w-12 h-8 rounded-full text-sm font-medium transition-all z-10",
mode === m.value ? "text-primary" : "text-muted-foreground"
)}
onClick={() => setMode(m.value)}
type="button"
>
{m.icon}
</button>
))}
</div>
</div>
);
}
// 总组件:根据设备类型渲染
export function ThemeModeToggle(props: React.HTMLAttributes<HTMLElement> = {}) {
const { isMobile, mode, setMode } = useDevice();
const Comp: React.ElementType = isMobile ? ThemeModeSegmented : ThemeModeCycleButton;
const { className, style } = props;
// 仅转发 className / style避免复杂的 prop 类型不匹配
return <Comp mode={mode} setMode={setMode} className={className} style={style} />;
}