123 lines
4.9 KiB
TypeScript
123 lines
4.9 KiB
TypeScript
import { ArrowRight, Loader2, Trash2 } from "lucide-react";
|
||
import type { Ref } from "react";
|
||
import type { CanvasProject } from "./types";
|
||
import { formatRelativeTime } from "./utils";
|
||
|
||
type LovartProjectsSectionProps = {
|
||
deletingProjectId: string | null;
|
||
hasMoreProjects: boolean;
|
||
isProjectsOnlyView: boolean;
|
||
onCreateProject: () => void;
|
||
onDeleteProject: (projectId: string, projectTitle?: string) => void;
|
||
onOpenProject: (projectId: string) => void;
|
||
projects: CanvasProject[];
|
||
projectSectionRef: Ref<HTMLElement>;
|
||
setShowAllProjects: (updater: (value: boolean) => boolean) => void;
|
||
showAllProjects: boolean;
|
||
};
|
||
|
||
export function LovartProjectsSection({
|
||
deletingProjectId,
|
||
hasMoreProjects,
|
||
isProjectsOnlyView,
|
||
onCreateProject,
|
||
onDeleteProject,
|
||
onOpenProject,
|
||
projects,
|
||
projectSectionRef,
|
||
setShowAllProjects,
|
||
showAllProjects,
|
||
}: LovartProjectsSectionProps) {
|
||
return (
|
||
<section
|
||
ref={projectSectionRef}
|
||
className={isProjectsOnlyView ? "mt-6" : "mt-20"}
|
||
>
|
||
<div className="mb-4 flex items-center justify-between">
|
||
<h2 className="text-[18px] font-semibold tracking-tight">
|
||
{isProjectsOnlyView ? "所有项目" : "最近项目"}
|
||
</h2>
|
||
{!isProjectsOnlyView && hasMoreProjects ? (
|
||
<button
|
||
type="button"
|
||
className="inline-flex items-center gap-1 text-[13px] text-black/45 hover:text-black dark:text-white/45 dark:hover:text-white"
|
||
onClick={() => setShowAllProjects((value) => !value)}
|
||
>
|
||
{showAllProjects ? "收起" : "查看更多"}
|
||
<ArrowRight
|
||
className={`h-3.5 w-3.5 transition-transform ${showAllProjects ? "rotate-90" : ""}`}
|
||
/>
|
||
</button>
|
||
) : null}
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-4">
|
||
<button
|
||
type="button"
|
||
onClick={onCreateProject}
|
||
className="cursor-pointer rounded-[22px] border border-black/8 bg-white p-3 text-left shadow-sm transition hover:-translate-y-0.5 dark:border-white/10 dark:bg-[#141416]"
|
||
>
|
||
<div className="flex aspect-[4/3] items-center justify-center rounded-2xl bg-[#efefef] dark:bg-[#1b1c20]">
|
||
<div className="text-center">
|
||
<div className="mb-2 text-3xl">+</div>
|
||
<div className="text-[14px]">新建项目</div>
|
||
</div>
|
||
</div>
|
||
</button>
|
||
|
||
{projects.map((item) => (
|
||
<div
|
||
key={item.id}
|
||
className="group relative rounded-[22px] border border-black/8 bg-white p-3 text-left shadow-sm transition hover:-translate-y-0.5 dark:border-white/10 dark:bg-[#141416]"
|
||
>
|
||
<button
|
||
type="button"
|
||
onClick={() => onOpenProject(item.id)}
|
||
className="block w-full cursor-pointer text-left"
|
||
>
|
||
<div className="aspect-[4/3] overflow-hidden rounded-2xl bg-[#efefef] dark:bg-[#1b1c20]">
|
||
{item.thumbnail ? (
|
||
<img
|
||
src={item.thumbnail}
|
||
alt={item.title}
|
||
className="h-full w-full object-cover transition-transform duration-300 ease-out will-change-transform group-hover:scale-[1.045]"
|
||
/>
|
||
) : (
|
||
<div className="flex h-full w-full items-center justify-center text-black/35 dark:text-white/35">
|
||
Untitled
|
||
</div>
|
||
)}
|
||
</div>
|
||
<div className="mt-3 text-[14px] font-medium">
|
||
{item.title || "Untitled"}
|
||
</div>
|
||
<div className="mt-1 text-xs text-black/35 dark:text-white/35">
|
||
{formatRelativeTime(item.updatedAt || item.createdAt)}
|
||
</div>
|
||
</button>
|
||
<button
|
||
type="button"
|
||
onClick={() => onDeleteProject(item.id, item.title)}
|
||
disabled={deletingProjectId === item.id}
|
||
className="absolute right-5 top-5 z-20 inline-flex h-7 w-7 items-center justify-center rounded-full border border-black/10 bg-white/90 text-black/45 shadow-sm transition hover:border-[#ffb4b4] hover:text-[#ef4444] disabled:cursor-not-allowed disabled:opacity-80 dark:border-white/10 dark:bg-[#18181c]/90 dark:text-white/45"
|
||
aria-label={`删除${item.title || "未命名画布"}`}
|
||
>
|
||
{deletingProjectId === item.id ? (
|
||
<Loader2 className="h-3.5 w-3.5 animate-spin" />
|
||
) : (
|
||
<Trash2 className="h-3.5 w-3.5" />
|
||
)}
|
||
</button>
|
||
</div>
|
||
))}
|
||
|
||
{projects.length === 0 && (
|
||
<div className="rounded-[22px] border border-dashed border-black/15 bg-white p-6 text-center text-black/40 md:col-span-3 dark:border-white/12 dark:bg-[#141416] dark:text-white/40">
|
||
还没有项目,点击“新建项目”开始。
|
||
</div>
|
||
)}
|
||
</div>
|
||
</section>
|
||
);
|
||
}
|