- 新增事件总线式toast通知系统 - 新增头像、阅读进度、404/500错误页等基础组件 - 重构页脚、布局、分页、点赞按钮等现有组件 - 新增用户个人页、通知中心、帖子编辑/发布/置顶/推荐管理功能 - 优化登录注册页面样式与交互 - 修复路径类型文件引用,更新站点元信息与字体配置
67 lines
2.0 KiB
TypeScript
67 lines
2.0 KiB
TypeScript
"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>
|
|
);
|
|
}
|