feat: 新增大量社区功能与基础组件,优化站点整体体验
- 新增事件总线式toast通知系统 - 新增头像、阅读进度、404/500错误页等基础组件 - 重构页脚、布局、分页、点赞按钮等现有组件 - 新增用户个人页、通知中心、帖子编辑/发布/置顶/推荐管理功能 - 优化登录注册页面样式与交互 - 修复路径类型文件引用,更新站点元信息与字体配置
This commit is contained in:
66
frontend/components/Toaster.tsx
Normal file
66
frontend/components/Toaster.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { X, AlertCircle, CheckCircle2 } from "lucide-react";
|
||||
import type { ToastDetail } from "@/lib/toast";
|
||||
|
||||
export default function Toaster() {
|
||||
const [items, setItems] = useState<ToastDetail[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const handler = (e: Event) => {
|
||||
const detail = (e as CustomEvent<ToastDetail>).detail;
|
||||
setItems((list) => [...list.slice(-2), detail]);
|
||||
window.setTimeout(() => {
|
||||
setItems((list) => list.filter((t) => t.id !== detail.id));
|
||||
}, 3600);
|
||||
};
|
||||
window.addEventListener("j13-toast", handler);
|
||||
return () => window.removeEventListener("j13-toast", handler);
|
||||
}, []);
|
||||
|
||||
const dismiss = (id: number) => setItems((list) => list.filter((t) => t.id !== id));
|
||||
|
||||
return (
|
||||
<div
|
||||
className="fixed bottom-5 right-4 sm:right-6 z-[10000] flex flex-col gap-2 w-[calc(100vw-2rem)] sm:w-80"
|
||||
aria-live="polite"
|
||||
role="status"
|
||||
>
|
||||
{items.map((t) => (
|
||||
<ToastCard key={t.id} toast={t} onClose={() => dismiss(t.id)} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ToastCard({ toast, onClose }: { toast: ToastDetail; onClose: () => void }) {
|
||||
const color = toast.variant === "error" ? "var(--danger)" : "var(--ok)";
|
||||
return (
|
||||
<div
|
||||
className="j13-toast-in flex items-start gap-3 px-4 py-3.5"
|
||||
style={{
|
||||
background: "var(--panel)",
|
||||
border: "1px solid var(--line)",
|
||||
borderLeft: `3px solid ${color}`,
|
||||
borderRadius: "var(--r-sm)",
|
||||
boxShadow: "var(--shadow-lg)",
|
||||
}}
|
||||
role="alert"
|
||||
>
|
||||
<span className="shrink-0 mt-0.5" style={{ color }}>
|
||||
{toast.variant === "error" ? <AlertCircle size={16} /> : <CheckCircle2 size={16} />}
|
||||
</span>
|
||||
<p className="flex-1 text-[13.5px] leading-snug" style={{ color: "var(--ink)" }}>
|
||||
{toast.message}
|
||||
</p>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="shrink-0 meta hover:text-[var(--ink)] transition-colors"
|
||||
aria-label="关闭提示"
|
||||
>
|
||||
<X size={14} />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user