mirror of https://github.com/usememos/memos
refactor: replace MemoSkeleton with a new Skeleton component for improved loading states
parent
792d58b74d
commit
115d1bacd7
@ -1,56 +0,0 @@
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface Props {
|
||||
showCreator?: boolean;
|
||||
count?: number;
|
||||
}
|
||||
|
||||
const MemoSkeleton = ({ showCreator = false, count = 6 }: Props) => {
|
||||
return (
|
||||
<>
|
||||
{Array.from({ length: count }).map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="relative flex flex-col justify-start items-start bg-card w-full max-w-2xl mx-auto px-4 py-3 mb-2 gap-2 rounded-lg border border-border animate-pulse"
|
||||
>
|
||||
{/* Header section */}
|
||||
<div className="w-full flex flex-row justify-between items-center gap-2">
|
||||
<div className="w-auto max-w-[calc(100%-8rem)] grow flex flex-row justify-start items-center">
|
||||
{showCreator ? (
|
||||
<div className="w-full flex flex-row justify-start items-center gap-2">
|
||||
{/* Avatar skeleton */}
|
||||
<div className="w-8 h-8 rounded-full bg-muted shrink-0" />
|
||||
<div className="w-full flex flex-col justify-center items-start gap-1">
|
||||
{/* Creator name skeleton */}
|
||||
<div className="h-4 w-24 bg-muted rounded" />
|
||||
{/* Timestamp skeleton */}
|
||||
<div className="h-3 w-16 bg-muted rounded" />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="h-4 w-32 bg-muted rounded" />
|
||||
)}
|
||||
</div>
|
||||
{/* Action buttons skeleton */}
|
||||
<div className="flex flex-row gap-2">
|
||||
<div className="w-4 h-4 bg-muted rounded" />
|
||||
<div className="w-4 h-4 bg-muted rounded" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content section */}
|
||||
<div className="w-full flex flex-col gap-2">
|
||||
{/* Text content skeleton - varied heights for realism */}
|
||||
<div className="space-y-2">
|
||||
<div className={cn("h-4 bg-muted rounded", index % 3 === 0 ? "w-full" : index % 3 === 1 ? "w-4/5" : "w-5/6")} />
|
||||
<div className={cn("h-4 bg-muted rounded", index % 2 === 0 ? "w-3/4" : "w-4/5")} />
|
||||
{index % 2 === 0 && <div className="h-4 w-2/3 bg-muted rounded" />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default MemoSkeleton;
|
||||
@ -0,0 +1,80 @@
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface Props {
|
||||
type?: "route" | "memo" | "pagination";
|
||||
showCreator?: boolean;
|
||||
count?: number;
|
||||
showEditor?: boolean;
|
||||
}
|
||||
|
||||
// Memo card skeleton component
|
||||
const MemoCardSkeleton = ({ showCreator = false, index = 0 }: { showCreator?: boolean; index?: number }) => (
|
||||
<div className="relative flex flex-col justify-start items-start bg-card w-full px-4 py-3 mb-2 gap-2 rounded-lg border border-border animate-pulse">
|
||||
{/* Header section */}
|
||||
<div className="w-full flex flex-row justify-between items-center gap-2">
|
||||
<div className="w-auto max-w-[calc(100%-8rem)] grow flex flex-row justify-start items-center">
|
||||
{showCreator ? (
|
||||
<div className="w-full flex flex-row justify-start items-center gap-2">
|
||||
<div className="w-8 h-8 rounded-full bg-muted shrink-0" />
|
||||
<div className="w-full flex flex-col justify-center items-start gap-1">
|
||||
<div className="h-4 w-24 bg-muted rounded" />
|
||||
<div className="h-3 w-16 bg-muted rounded" />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="h-4 w-32 bg-muted rounded" />
|
||||
)}
|
||||
</div>
|
||||
{/* Action buttons skeleton */}
|
||||
<div className="flex flex-row gap-2">
|
||||
<div className="w-4 h-4 bg-muted rounded" />
|
||||
<div className="w-4 h-4 bg-muted rounded" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content section */}
|
||||
<div className="w-full flex flex-col gap-2">
|
||||
<div className="space-y-2">
|
||||
<div className={cn("h-4 bg-muted rounded", index % 3 === 0 ? "w-full" : index % 3 === 1 ? "w-4/5" : "w-5/6")} />
|
||||
<div className={cn("h-4 bg-muted rounded", index % 2 === 0 ? "w-3/4" : "w-4/5")} />
|
||||
{index % 2 === 0 && <div className="h-4 w-2/3 bg-muted rounded" />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const Skeleton = ({ type = "route", showCreator = false, count = 4, showEditor = true }: Props) => {
|
||||
// Pagination type: simpler, just memos
|
||||
if (type === "pagination") {
|
||||
return (
|
||||
<div className="w-full flex flex-col justify-center items-center my-4">
|
||||
<div className="w-full max-w-2xl mx-auto">
|
||||
{Array.from({ length: count }).map((_, index) => (
|
||||
<MemoCardSkeleton key={index} showCreator={showCreator} index={index} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Route or memo type: with optional wrapper
|
||||
return (
|
||||
<div className="w-full max-w-full px-4 py-6">
|
||||
<div className="w-full max-w-2xl mx-auto">
|
||||
{/* Editor skeleton - only for route type */}
|
||||
{type === "route" && showEditor && (
|
||||
<div className="relative flex flex-col justify-start items-start bg-card w-full px-4 py-3 mb-4 gap-2 rounded-lg border border-border animate-pulse">
|
||||
<div className="w-full h-12 bg-muted rounded" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Memo skeletons */}
|
||||
{Array.from({ length: count }).map((_, index) => (
|
||||
<MemoCardSkeleton key={index} showCreator={showCreator} index={index} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Skeleton;
|
||||
@ -0,0 +1,19 @@
|
||||
import { LoaderIcon } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
size?: "sm" | "md" | "lg";
|
||||
}
|
||||
|
||||
const Spinner = ({ className, size = "md" }: Props) => {
|
||||
const sizeClasses = {
|
||||
sm: "w-4 h-4",
|
||||
md: "w-6 h-6",
|
||||
lg: "w-8 h-8",
|
||||
};
|
||||
|
||||
return <LoaderIcon className={cn("animate-spin", sizeClasses[size], className)} />;
|
||||
};
|
||||
|
||||
export default Spinner;
|
||||
@ -1,13 +0,0 @@
|
||||
import { LoaderIcon } from "lucide-react";
|
||||
|
||||
function Loading() {
|
||||
return (
|
||||
<div className="fixed w-full h-full flex flex-row justify-center items-center">
|
||||
<div className="w-80 max-w-full h-full py-4 flex flex-col justify-center items-center">
|
||||
<LoaderIcon className="animate-spin text-foreground" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Loading;
|
||||
Loading…
Reference in New Issue