Skip to content

Commit 7734ca0

Browse files
committed
Add a button to copy the currently viewed article's link
1 parent 4689d38 commit 7734ca0

1 file changed

Lines changed: 56 additions & 1 deletion

File tree

src/components/tutorial/TutorialWindow.tsx

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Tabs, TabsContent } from "@radix-ui/react-tabs";
22
import { useNavigate, useRouter, useSearch } from "@tanstack/react-router";
3-
import { DatabaseZapIcon, XIcon } from "lucide-react";
3+
import { CheckIcon, DatabaseZapIcon, LinkIcon, XIcon } from "lucide-react";
44
import { AnimatePresence, motion } from "motion/react";
55
import {
66
useCallback,
@@ -22,6 +22,60 @@ import { ScrollArea } from "../ui/scroll-area";
2222
import { ScrollShadow } from "../ui/scroll-shadow";
2323
import { TutorialTableOfContents } from "./TutorialTableOfContents";
2424

25+
function CopyArticleLinkButton({ activeStep }: { activeStep: string | null }) {
26+
const [copied, setCopied] = useState(false);
27+
28+
const copyLink = useCallback(() => {
29+
if (!activeStep) return;
30+
31+
const url = new URL(window.location.href);
32+
url.searchParams.set(
33+
"article",
34+
encodeURIComponent(activeStep.toLowerCase()),
35+
);
36+
37+
navigator.clipboard.writeText(url.toString()).then(() => {
38+
setCopied(true);
39+
setTimeout(() => setCopied(false), 2000);
40+
});
41+
}, [activeStep]);
42+
43+
return (
44+
<Button
45+
variant="outline"
46+
size="sm"
47+
onClick={copyLink}
48+
className="absolute top-2 right-4 z-20"
49+
title="Copy link to this article"
50+
>
51+
<AnimatePresence mode="wait" initial={false}>
52+
{copied ? (
53+
<motion.div
54+
key="check"
55+
initial={{ scale: 0.5, opacity: 0 }}
56+
animate={{ scale: 1, opacity: 1 }}
57+
exit={{ scale: 0.5, opacity: 0 }}
58+
transition={{ duration: 0.15 }}
59+
>
60+
<CheckIcon className="h-4 w-4 text-green-600" />
61+
</motion.div>
62+
) : (
63+
<motion.div
64+
key="link"
65+
initial={{ scale: 0.5, opacity: 0 }}
66+
animate={{ scale: 1, opacity: 1 }}
67+
exit={{ scale: 0.5, opacity: 0 }}
68+
transition={{ duration: 0.15 }}
69+
>
70+
<LinkIcon className="h-4 w-4" />
71+
</motion.div>
72+
)}
73+
</AnimatePresence>
74+
{copied ? "Copied!" : "Copy link"}
75+
</Button>
76+
);
77+
}
78+
2579
function FloatingWindowHeader({ toggleWindow }: { toggleWindow: () => void }) {
2680
return (
2781
<div className="text-sm bg-linear-to-r bg-primary text-primary-foreground border-b border-border dark:border-b-primary px-2 py-1 flex items-center">
@@ -365,6 +419,7 @@ function FloatingWindow({
365419
isResizing && "pointer-events-none",
366420
)}
367421
>
422+
<CopyArticleLinkButton activeStep={activeStep} />
368423
<ScrollShadow
369424
position="top"
370425
visible={canScrollUp}

0 commit comments

Comments
 (0)