Skip to content

Commit

Permalink
style: 💄 copy
Browse files Browse the repository at this point in the history
  • Loading branch information
zhuba-Ahhh committed Aug 24, 2024
1 parent 42006b0 commit f6abd3c
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 68 deletions.
1 change: 1 addition & 0 deletions public/svg/check-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion public/svg/check.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions public/svg/copy-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion public/svg/copy.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
70 changes: 30 additions & 40 deletions src/components/Comments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,48 +17,36 @@ interface Comment {
author: string;
content: string;
date: string;
replies: Comment[];
replies?: Comment[];
}

type NewComment = Pick<Comment, "author" | "content">;

export default function Comments() {
const [comments, setComments] = useState<Comment[]>([]);
const [newComment, setNewComment] = useState({ author: "", content: "" });
const [newComment, setNewComment] = useState<NewComment>({
author: "",
content: "",
});
const [sortOrder, setSortOrder] = useState<"newest" | "oldest">("newest");

const sortedComments = useMemo(() => {
return [...comments].sort((a, b) => {
return sortOrder === "newest"
? new Date(b.date).getTime() - new Date(a.date).getTime()
: new Date(a.date).getTime() - new Date(b.date).getTime();
});
}, [comments, sortOrder]);

const addReply = useCallback((parentId: number, replyContent: string) => {
setComments((prevComments) => {
const newComments = [...prevComments];
const parentComment = newComments.find((c) => c.id === parentId);
if (parentComment) {
parentComment.replies.push({
id: Date.now(),
author: "匿名用户",
content: replyContent,
date: new Date().toLocaleString(),
replies: [],
});
if (sortOrder === "newest") {
return new Date(b.date).getTime() - new Date(a.date).getTime();
} else {
return new Date(a.date).getTime() - new Date(b.date).getTime();
}
return newComments;
});
}, []);
}, [comments, sortOrder]);

const handleSubmit = useCallback(
(e: React.FormEvent) => {
(e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const comment: Comment = {
...newComment,
id: Date.now(),
author: newComment.author.trim(),
content: newComment.content.trim(),
date: new Date().toLocaleString(),
replies: [],
date: new Date().toISOString(),
};
setComments((prevComments) => [...prevComments, comment]);
setNewComment({ author: "", content: "" });
Expand Down Expand Up @@ -116,19 +104,21 @@ export default function Comments() {
<CardContent>
<p>{comment.content}</p>
</CardContent>
<CardFooter>
{comment.replies.map((reply) => (
<Card key={reply.id} className="mt-2 w-full">
<CardHeader>
<CardTitle>{reply.author}</CardTitle>
<CardDescription>{reply.date}</CardDescription>
</CardHeader>
<CardContent>
<p>{reply.content}</p>
</CardContent>
</Card>
))}
</CardFooter>
{comment?.replies && comment?.replies?.length > 0 && (
<CardFooter>
{comment?.replies?.map((reply) => (
<Card key={reply.id} className="mt-2 w-full">
<CardHeader>
<CardTitle>{reply.author}</CardTitle>
<CardDescription>{reply.date}</CardDescription>
</CardHeader>
<CardContent>
<p>{reply.content}</p>
</CardContent>
</Card>
))}
</CardFooter>
)}
</Card>
))}
</div>
Expand Down
72 changes: 46 additions & 26 deletions src/components/CopyButton.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
"use client";

import React, { useState, useCallback } from "react";
import React, { useState, useCallback, useMemo } from "react";
import Image from "next/image";
import { throttle } from "lodash-es";
import { useTheme } from "next-themes";

interface CopyButtonProps {
text: string;
Expand All @@ -11,35 +12,54 @@ interface CopyButtonProps {

const CopyButton: React.FC<CopyButtonProps> = ({ text, language }) => {
const [state, setState] = useState<"idle" | "copy" | "copied">("idle");
const { theme } = useTheme();

const throttledCopy = throttle(async () => {
if (state === "copied") return;
try {
await navigator.clipboard.writeText(text);
setState("copied");
setTimeout(() => setState("idle"), 1000);
} catch (err) {
console.error("复制失败:", err);
setState("idle");
}
}, 300);
const throttledCopy = useMemo(
() =>
throttle(async () => {
if (state === "copied") return;
try {
await navigator.clipboard.writeText(text);
setState("copied");
setTimeout(() => setState("idle"), 1000);
} catch (err) {
console.error("复制失败:", err);
setState("idle");
}
}, 300),
[text, state]
);

const handleCopy = useCallback(() => {
throttledCopy();
}, [throttledCopy]);

const buttonClassName = useMemo(
() => `
absolute top-2 right-2 p-1.5 rounded-md
bg-gray-100 dark:bg-gray-800
border border-gray-200 dark:border-gray-700
hover:bg-gray-200 dark:hover:bg-gray-700
transition-all duration-300 ease-in-out
flex items-center justify-center
shadow-sm hover:shadow-md dark:shadow-gray-900
h-[30px]
`,
[]
);

const handleCopy = useCallback(throttledCopy, [throttledCopy, text, state]);
const getImageSrc = (baseName: string) => {
return theme === "dark"
? `/svg/${baseName}-dark.svg`
: `/svg/${baseName}.svg`;
};

return (
<button
onClick={handleCopy}
onMouseEnter={() => setState((prev) => (prev === "idle" ? "copy" : prev))}
onMouseLeave={() => setState((prev) => (prev === "copy" ? "idle" : prev))}
className={`
absolute top-2 right-2 p-1.5 rounded-md
bg-gray-200 dark:bg-gray-800
border border-gray-300 dark:border-gray-700
hover:bg-gray-300 dark:hover:bg-gray-700
transition-all duration-300 ease-in-out
flex items-center justify-center
shadow-sm hover:shadow-md dark:shadow-gray-900
`}
className={buttonClassName}
title={state === "idle" ? language : state === "copy" ? "复制" : "已复制"}
>
{state === "idle" && (
Expand All @@ -49,16 +69,16 @@ const CopyButton: React.FC<CopyButtonProps> = ({ text, language }) => {
)}
{state === "copy" && (
<Image
src="/svg/copy.svg"
src={getImageSrc("copy")}
alt="复制"
width={18}
height={18}
className="text-gray-700 dark:text-gray-300"
// className="text-gray-700 dark:text-gray-300"
/>
)}
{state === "copied" && (
<Image
src="/svg/check.svg"
src={getImageSrc("check")}
alt="已复制"
width={18}
height={18}
Expand All @@ -69,4 +89,4 @@ const CopyButton: React.FC<CopyButtonProps> = ({ text, language }) => {
);
};

export default CopyButton;
export default CopyButton;

0 comments on commit f6abd3c

Please sign in to comment.